Устанавливаем необходимые библиотки

library(tidyverse)
library(DESeq2)
library(pheatmap)
library(RColorBrewer)
library(clusterProfiler)
library(biomaRt)
library(org.Hs.eg.db)
library(EnhancedVolcano)
library(GenomicRanges)
library(msigdbr)
library(multiMiR)
library(miRBaseConverter)
library(enrichplot)
library(vsn)
library(rvest)
library(patchwork)
library(dbplyr)

Импортируем данные

coldata <- read_tsv("data/phenotable.tsv", show_col_types = FALSE)
rownames(coldata) <- coldata$sample
Warning: Setting row names on a tibble is deprecated.
coldata
counts <- read.csv("data/miR.Counts.csv", header = TRUE, sep = ",")
counts <- column_to_rownames(counts, var = "miRNA")
head(counts)
colnames(counts) <- gsub("^X", "", colnames(counts))

counts_samples <- colnames(counts)
phenotable_samples <- coldata$sample
common_samples <- intersect(counts_samples, phenotable_samples)

counts <- counts[, c(counts$miRNA, common_samples)]  
counts <- counts[, rownames(coldata)] #ранжирую по колонки в counts так же как и названия строк в coldata
head(counts)
anno <- read.csv("data/annotation.report.csv", header = TRUE, sep = ",")
anno$Sample.name.s. <- gsub("-", ".", anno$Sample.name.s.)
anno <- anno[, -c(2:5, 7, 15)]


common_samples <- intersect(anno$Sample.name.s., coldata$sample)

anno <- anno[anno$Sample.name.s. %in% common_samples, ]
anno <- anno[match(rownames(coldata), anno$Sample.name.s.), ] #ранжирую по колонки в counts так же как и названия строк в coldata
anno

Весь датасет

anno_long <- anno %>%
  pivot_longer(cols = -Sample.name.s., names_to = "RNA_Type", values_to = "Count")

plt <- ggplot(anno_long, aes(x = Sample.name.s., y = Count, fill = RNA_Type)) +
  geom_bar(stat = "identity") +
  theme_minimal() +
  labs(x = "Sample", y = "Read Count") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_fill_brewer(palette = "Set3")  # Красивые цвета

print(plt)
ggsave("./pictures_transpl/transpl_barplot_alldataset_no_normalised.tiff", plot = plt, width = 8, height = 6, dpi = 300,  bg = "white")

anno_long <- anno %>%
  rowwise() %>%
  mutate(across(-Sample.name.s., ~ . / sum(c_across(-Sample.name.s.)))) %>% 
  ungroup() %>%
  pivot_longer(cols = -Sample.name.s., names_to = "RNA_Type", values_to = "Proportion")

plt <- ggplot(anno_long, aes(x = Sample.name.s., y = Proportion, fill = RNA_Type)) +
  geom_bar(stat = "identity") +
  theme_minimal() +
  labs(x = "Sample", y = "Proportion") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  scale_fill_brewer(palette = "Set3")

plt
ggsave("./pictures_transpl/transpl_barplot_alldataset_normalised.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

coldata$condition <- relevel(factor(coldata$condition), ref = "no_complications")
modelMatrix <- model.matrix(~condition, coldata)
modelMatrix
   (Intercept) conditioncellular conditionhumoral conditionTCAD
1            1                 0                0             0
2            1                 0                0             0
3            1                 0                1             0
4            1                 1                0             0
5            1                 0                0             1
6            1                 0                0             1
7            1                 0                0             1
8            1                 0                0             0
9            1                 0                0             0
10           1                 0                0             0
11           1                 0                0             0
12           1                 0                1             0
13           1                 0                1             0
14           1                 1                0             0
15           1                 1                0             0
16           1                 1                0             0
17           1                 1                0             0
18           1                 1                0             0
19           1                 0                0             1
20           1                 0                0             1
21           1                 0                1             0
22           1                 0                0             1
23           1                 0                0             1
24           1                 0                1             0
25           1                 0                1             0
26           1                 0                0             0
27           1                 0                0             0
28           1                 0                1             0
29           1                 0                0             0
attr(,"assign")
[1] 0 1 1 1
attr(,"contrasts")
attr(,"contrasts")$condition
[1] "contr.treatment"
qr(modelMatrix)$rank  # ранг матрицы
[1] 4
ncol(modelMatrix) 
[1] 4
dds <- DESeqDataSetFromMatrix(countData = counts, colData = coldata, design = ~ condition)
converting counts to integer mode
dds$condition <- relevel(dds$condition, ref = "no_complications")
dds
class: DESeqDataSet 
dim: 913 29 
metadata(1): version
assays(1): counts
rownames(913): Hsa-Let-7-P1a_3p* Hsa-Let-7-P1a_5p/P2a1_5p/P2a2_5p ... Hsa-Mir-9851_3p Hsa-Mir-9851_5p*
rowData names(0):
colnames(29): 105_S1_R1_001 197_S2_R1_001 ... 127_S29_R1_001 138_S30_R1_001
colData names(2): sample condition
dim(dds)
[1] 913  29
smallestGroupSize <- 15
keep <- rowSums(counts(dds) >= 10) >= smallestGroupSize
dds <- dds[keep,]
dim(dds)
[1] 297  29

Run Differential Expression Analysis

dds <- DESeq(dds, fitType = "parametric")
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 31 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
dds
class: DESeqDataSet 
dim: 297 29 
metadata(1): version
assays(6): counts mu ... replaceCounts replaceCooks
rownames(297): Hsa-Let-7-P1a_5p/P2a1_5p/P2a2_5p Hsa-Let-7-P1b_5p ... Hsa-Mir-96-P3_3p* Hsa-Mir-96-P3_5p
rowData names(31): baseMean baseVar ... maxCooks replace
colnames(29): 105_S1_R1_001 197_S2_R1_001 ... 127_S29_R1_001 138_S30_R1_001
colData names(4): sample condition sizeFactor replaceable
plotDispEsts(dds)

raw_counts <- counts(dds, normalized = FALSE)
normalized_counts <- counts(dds, normalized = TRUE)

df <- data.frame(
  Sample = rep(colnames(dds), 2),
  Counts = c(colSums(raw_counts), colSums(normalized_counts)),
  Type = rep(c("Raw", "Normalized"), each = ncol(dds))
)

plt <- ggplot(df, aes(x = Sample, y = Counts, fill = Type)) +
  geom_bar(stat = "identity", position = "dodge") +
  theme_minimal() +
  labs(title = "Counts before and after normalization", x = "Sample", y = "Total Counts") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

plt
ggsave("./pictures_transpl/transpl_Counts before and after normalization.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

rlog трансформация

rlt <- rlog(dds)  #rlog Transformation
meanSdPlot(assay(rlt)) 

vsd <- varianceStabilizingTransformation(dds, blind=FALSE) 
meanSdPlot(assay(vsd)) #показывает, как изменяется стандартное отклонение в зависимости от среднего значения экспрессии

** PCA plot **

pcaData <- plotPCA(rlt, intgroup=c("condition"), returnData = TRUE)
using ntop=500 top features by variance
percentVar <- round(100 * attr(pcaData, "percentVar"))

pcaData$sample <- gsub("_.*", "", coldata$sample)


plt <- ggplot(pcaData, aes(PC1, PC2, color = condition)) +
  geom_text(aes(label=sample), size=3, vjust=1.5) +
  geom_point(size = 3) +
  xlab(paste0("PC1: ", percentVar[1], "%")) +
  ylab(paste0("PC2: ", percentVar[2], "%")) + 
  coord_fixed() +
  theme_bw() +
  scale_color_brewer(palette = "Set2")
plt
ggsave("./pictures_transpl/transpl_PCA plot.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Plot a heatmap of 50 most expressed genes Этот heatmap отражает уровни экспрессии генов, а не разницу между группами. Цвета не означают up- или down-регуляцию в сравнении с контрольной группой, потому что heatmap показывает абсолютные значения экспрессии, а не fold change!

select <- order(rowMeans(counts(dds,normalized=TRUE)),
                decreasing=TRUE)[1:50]
df <- as.data.frame(colData(dds)$condition)
colnames(df) <- "condition"
rownames(df) <- colnames(counts(dds))
plt <- pheatmap(assay(rlt)[select,], 
         cluster_rows = TRUE, 
         show_rownames = TRUE, 
         cluster_cols = TRUE, 
         annotation_col = df,
         fontsize_row = 6) 

plt

ggsave("./pictures_transpl/transpl_Plot a heatmap of 50 most expressed genes.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Plot of the distance between samples heatmap Расчет расстояний между образцами • Обычно используется евклидово расстояние (по умолчанию в DESeq2). • Оно вычисляется по нормализованным данным экспрессии (rlog() или vst()). • Чем меньше расстояние — тем более похожи образцы.

sampleDists <- dist(t(assay(rlt)))
sampleDistMatrix <- as.matrix(sampleDists)
rownames(sampleDistMatrix) <- paste(rlt$condition)
colnames(sampleDistMatrix) <- paste(rlt$condition)
colors <- colorRampPalette(rev(brewer.pal(9, "Blues")) )(255)

plt <- pheatmap(sampleDistMatrix,
         clustering_distance_rows = "euclidean",
         clustering_distance_cols = "euclidean",
         color = colors)

plt

ggsave("./pictures_transpl/transpl_Plot of the distance between samples heatmap.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")
res_humoral <- results(dds, contrast=c("condition", "no_complications", "humoral"))
res_humoral
log2 fold change (MLE): condition no_complications vs humoral 
Wald test p-value: condition no_complications vs humoral 
DataFrame with 297 rows and 6 columns
                                    baseMean log2FoldChange     lfcSE      stat    pvalue      padj
                                   <numeric>      <numeric> <numeric> <numeric> <numeric> <numeric>
Hsa-Let-7-P1a_5p/P2a1_5p/P2a2_5p   72441.408      0.0618842  0.288818  0.214267 0.8303389  0.939409
Hsa-Let-7-P1b_5p                    2471.713     -0.9029022  0.495172 -1.823412 0.0682410  0.396094
Hsa-Let-7-P1c_5p                    1586.095     -1.0840729  0.471749 -2.297986 0.0215626  0.246039
Hsa-Let-7-P2a1_3p*                   106.535      0.0898204  0.728328  0.123324 0.9018504  0.974875
Hsa-Let-7-P2a2_3p*                   117.677     -2.1053617  1.182746 -1.780062 0.0750658  0.409032
...                                      ...            ...       ...       ...       ...       ...
Hsa-Mir-95-P3_5p                     11.6942       1.248042  1.169651  1.067020 0.2859626        NA
Hsa-Mir-96-P1_5p                    452.3189       1.016073  0.712354  1.426361 0.1537643 0.6037509
Hsa-Mir-96-P2_5p                 104065.5500      -0.309282  0.550995 -0.561315 0.5745829 0.8960804
Hsa-Mir-96-P3_3p*                    27.6400       0.953532  0.804588  1.185117 0.2359712 0.7233923
Hsa-Mir-96-P3_5p                   1433.3806       1.044344  0.364247  2.867132 0.0041421 0.0737293

MA plot Фильтрация точек с низким средним экспрессированием (по baseMean). • Обычно отсекаются baseMean < 1. 2. Определение значимых генов (синие точки): • Используется критерий padj < 0.1 по умолчанию, а не < 0.05!

tiff("./pictures_transpl/transpl_PlotMA_standart_padj_0.05_humoral.tiff", 
     width = 8, height = 6, units = "in", res = 300, bg = "white")
plotMA(res_humoral, alpha = 0.05, ylim = c(-8, 8)) 
dev.off()
null device 
          1 
plotMA(res_humoral, alpha = 0.05, ylim = c(-8, 8)) 

Кастомный MA plot

res_df <- res_humoral %>%
  as.data.frame() %>%
  mutate(color = case_when( 
    padj < 0.05  ~ "padj < 0.05",   
    pvalue < 0.05  ~ "pvalue < 0.05", 
    TRUE ~ "All"
  ))

plt <- ggplot(res_df, aes(x = baseMean, y = log2FoldChange, color = color)) +
  geom_point(alpha = 0.7, size = 1) +
  geom_hline(yintercept = 0, linetype = "solid", color = "gray40", size = 1.5) +
  scale_color_manual(values = c("All" = "gray70", 
                                "pvalue < 0.05" = "blue", 
                                "padj < 0.05" = "red")) +
  scale_x_log10(labels = scales::scientific) + 
  theme_minimal() +
  labs(x = "mean of normalized counts", 
       y = "log fold change", 
       color = NULL)

plt
ggsave("./pictures_transpl/transpl_Сustom MAplot_humoral.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Значимые результаты

58 генов (20%) имеют низкие уровни экспрессии и фильтруются из анализа. independent filtering — процедура, которая исключает гены с низкими значениями для увеличения статистической мощности.

signres_humoral <- results(dds, contrast=c("condition", "no_complications", "humoral"), alpha=0.05) 
summary(signres_humoral)

out of 297 with nonzero total read count
adjusted p-value < 0.05
LFC > 0 (up)       : 0, 0%
LFC < 0 (down)     : 9, 3%
outliers [1]       : 1, 0.34%
low counts [2]     : 58, 20%
(mean count < 40)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results

Let’s arranged it by log2FoldChange:

order_indices <- order(-res_humoral$log2FoldChange)
res_humoral[order_indices, ]
log2 fold change (MLE): condition no_complications vs humoral 
Wald test p-value: condition no_complications vs humoral 
DataFrame with 297 rows and 6 columns
                     baseMean log2FoldChange     lfcSE      stat      pvalue       padj
                    <numeric>      <numeric> <numeric> <numeric>   <numeric>  <numeric>
Hsa-Mir-542_3p       179.1170        1.55776  0.661653   2.35434   0.0185557   0.225199
Hsa-Mir-190-P1_5p     19.6983        1.54017  1.120054   1.37509   0.1691041         NA
Hsa-Mir-874_3p       183.9741        1.53509  0.798423   1.92265   0.0545243   0.378167
Hsa-Mir-624_5p        18.9670        1.36865  1.076852   1.27097   0.2037388         NA
Hsa-Mir-95-P3_5p      11.6942        1.24804  1.169651   1.06702   0.2859626         NA
...                       ...            ...       ...       ...         ...        ...
Hsa-Mir-873_3p       102.3872       -2.74537  1.229274  -2.23333 2.55274e-02 0.27263230
Hsa-Mir-10-P2a_5p   1162.4941       -2.85595  0.650038  -4.39350 1.11539e-05 0.00148904
Hsa-Mir-483_5p       144.9177       -3.86731  1.288810  -3.00068 2.69377e-03 0.06522233
Hsa-Mir-193-P1b_5p*  152.0458       -4.84885  0.982406  -4.93569 7.98689e-07 0.00021325
Hsa-Mir-193-P1b_3p    99.8971       -5.64893  1.644846  -3.43432 5.94044e-04 0.02643497

Visualisation for the first gene

#plotCounts(dds, gene=which.max(res_humoral$log2FoldChange), intgroup="condition")
plotCounts(dds, gene=which.min(res_humoral$padj), intgroup="condition")

#plotCounts(dds, gene=rownames(res)[which.min(res$padj[which.max(res$log2FoldChange)])], intgroup="condition")

Volcano plot

plt <- EnhancedVolcano(res_humoral,
                lab = rownames(res_humoral),
                x = "log2FoldChange",
                y = "padj",
                pCutoff = 0.05,
                FCcutoff = 1,
                labSize = 3.0,
                boxedLabels = FALSE,
                col = c('black', '#CBD5E8', '#B3E2CD', '#FDCDAC'),
                colAlpha = 1,
                title = NULL,        
                subtitle = NULL) 

plt
ggsave("./pictures_transpl/transpl_VolcanoPlot_humoral.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

coldata_filtered <- coldata[coldata$condition %in% c("humoral", "no_complications"), ]
coldata_filtered

Plot a heatmap of diff expressed genes

res_sign_humoral <- subset(res_humoral, padj < 0.05 & !is.na(padj) & abs(log2FoldChange) > 1.0)
res_sign_humoral <- res_sign_humoral[order(res_sign_humoral$log2FoldChange, decreasing = TRUE), ]

sig_genes <- rownames(res_sign_humoral)  

de_mat <- assay(rlt)[sig_genes, ] 

de_mat_filtered <- de_mat[, coldata_filtered$sample]
#datamatrix <- t(scale(t(de_mat_filtered)))
datamatrix <- de_mat_filtered

annotation_col <- data.frame(condition = coldata_filtered$condition)
rownames(annotation_col) <- colnames(datamatrix)

annotation_colors <- list(
  condition = c("no_complications" = "#FFCC00", "humoral" = "#3399FF"))

plt <- pheatmap(datamatrix,
         cluster_rows = TRUE, 
         show_rownames = TRUE, 
         cluster_cols = TRUE, 
         annotation_col = annotation_col,
         annotation_colors = annotation_colors,
         display_numbers = FALSE,
         legend = TRUE,
         fontsize = 15)  


ggsave("./pictures_transpl/transpl_Heatmap of diff expressed genes_humoral.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Анализ обогащения гуморальный ответ

up_humoral <- res_sign_humoral %>% 
  as.data.frame() %>% 
  filter(log2FoldChange > 0)
down_humoral <- res_sign_humoral %>% 
  as.data.frame() %>% 
  filter(log2FoldChange < 0)
rownames(up_humoral)
character(0)
rownames(down_humoral)
[1] "Hsa-Mir-146-P1_5p"   "Hsa-Mir-425_3p*"     "Hsa-Mir-148-P2_3p"   "Hsa-Mir-10-P3c_5p"   "Hsa-Mir-10-P2a_5p"  
[6] "Hsa-Mir-193-P1b_5p*" "Hsa-Mir-193-P1b_3p" 

Переводим в miRBase • miRBase: https://www.mirbase.org/ • MirGeneDB: https://mirgenedb.org/

url <- "https://mirgenedb.org/browse/hsa"
page <- read_html(url)

Парсим таблицу

mir_table <- page %>%
  html_element("table") %>%
  html_table(fill = TRUE) 

mir_table <- mir_table[-c(1:3), c(1,2) ] 
colnames(mir_table) <- c("MirGeneDB_ID", "MiRBase_ID")
mir_table$MirGeneDB_ID <- sub(" V", "", mir_table$MirGeneDB_ID)

head(mir_table)
down_humoral_clean <- sub("_.*", "", row.names(down_humoral))
down_humoral_converted <- mir_table$MiRBase_ID[match(down_humoral_clean, mir_table$MirGeneDB_ID)]
down_humoral_converted
[1] "hsa-mir-146b"   "hsa-mir-425"    NA               "hsa-mir-125b-2" NA               "hsa-mir-193a"  
[7] "hsa-mir-193a"  

Конвертация в MIMATID NA без соответствия удалила из анализа

NA Hsa-Mir-148-P2_3p есть три похожих соответствия: Hsa-Mir-148-P1 hsa-mir-148a
Hsa-Mir-148-P3 hsa-mir-152
Hsa-Mir-148-P4 hsa-mir-148b

NA Hsa-Mir-10-P2a_5p есть три похожих соответствие: Hsa-Mir-10-P2b hsa-mir-99b
Hsa-Mir-10-P2c hsa-mir-99a
Hsa-Mir-10-P2d hsa-mir-100

[1] “Hsa-Mir-146-P1_5p” “Hsa-Mir-425_3p” ”Hsa-Mir-148-P2_3p” ”Hsa-Mir-10-P3c_5p” ”Hsa-Mir-10-P2a_5p”
[6] ”Hsa-Mir-193-P1b_5p
” “Hsa-Mir-193-P1b_3p”

mirna_names_down <- c("hsa-miR-146b-5p", "hsa-miR-425-3p", "hsa-miR-125b-5p", "hsa-miR-193a-5p", "hsa-miR-193a-3p")
converted_mirna_down <- miRNAVersionConvert(mirna_names_down)
converted_mirna_down

Запрос таргетов из базы multiMiR

targets_humoral_down <- unique(get_multimir(org = "hsa", mirna = converted_mirna_down$Accession, table = "validated")@data$target_symbol)
Searching mirecords ...
Searching mirtarbase ...
Searching tarbase ...
#writeLines(targets_down, "targets_down150_list.txt")

Анализ обогащения из базы биологических процессов

#msig_go_bp <- msigdbr(species = "Homo sapiens", category = "C5", subcategory = "GO:BP")
# targets_down <- readLines("targets_down150_list.txt")
# targets_up <- readLines("targets_up150_list.txt")

GO_enrich_down_humoral_bp <- enrichGO(
  gene          = targets_humoral_down,  
  OrgDb         = org.Hs.eg.db,
  keyType       = "SYMBOL",
  ont           = "BP", 
  pAdjustMethod = "BH",
  qvalueCutoff  = 0.05
)

Визуализация


p1 <- dotplot(GO_enrich_down_humoral_bp, showCategory = 20) + 
  ggtitle("GO Enrichment for DOWNregulated targets") + 
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 11)
  )

p1

ggsave("./pictures_transpl/transpl_GO_enrichment_dotplot_down_humoral_bp.tiff", plot = p1, width = 16, height = 10, dpi = 300)

GO_enrich_DOWN_humoral_BP <- enrichplot::pairwise_termsim(GO_enrich_down_humoral_bp, method = "JC")

plt <- emapplot(GO_enrich_DOWN_humoral_BP, 
         repel = TRUE,
         showCategory = 20) +
  ggtitle("Biological processes for DOWNregulated targets for humoral") +
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text = element_text(size = 3)
    )    

plt

ggsave("./pictures_transpl/transpl_GO_enrichment_emapplot_DOWN_humoral_BP.tiff", plot = plt, width = 16, height = 10, dpi = 300)

GO_enrich_UP150_BP <- enrichplot::pairwise_termsim(GO_enrich_up150_bp, method = "JC")

plt <- emapplot(GO_enrich_UP150_BP, 
         repel = TRUE,
         showCategory = 20) +
  ggtitle("Biological processes for UPregulated targets for vesicles 150") +
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text = element_text(size = 3)
    )   

plt

ggsave("./pictures/GO_enrichment_emapplot_BPup_type150.tiff", plot = plt, width = 16, height = 10, dpi = 300)

Анализ обогащения из базы IMMUNESIGDB IMMUNESIGDB: Наборы генов, связанные с иммунной системой, включая иммуно-онкологию и другие аспекты иммунитета.

msig_go_bp <- msigdbr(species = "Homo sapiens", category = "C7", subcategory = "IMMUNESIGDB")

GO_enrich_up150_immum <- enricher(gene = targets150_up, TERM2GENE = msig_go_bp[, c("gs_name", "gene_symbol")])
GO_enrich_down150_immun <- enricher(gene = targets150_down, TERM2GENE = msig_go_bp[, c("gs_name", "gene_symbol")])

Визуализация IMMUNESIGDB


p1 <- dotplot(GO_enrich_up150_immum, showCategory = 20) + 
  ggtitle("GO Enrichment IMMUNESIGD for UPregulated targets for vesicles 150") + 
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 8)
  )

p2 <- dotplot(GO_enrich_down150_immun, showCategory = 20) + 
  ggtitle("GO Enrichment IMMUNESIGD for DOWNregulated targets for vesicles 150") + 
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 8)
  )

p1 + p2

combined_plot <- p1 + p2

ggsave("./pictures/GO_enrichment_dotplot_IMMUNESIGD_type150.tiff", plot = combined_plot, width = 16, height = 10, dpi = 300)

Анализ обогащения из базы KEGG

msig_go_bp <- msigdbr(species = "Homo sapiens", category = "C2", subcategory = "CP:KEGG")

GO_enrich_up150_KEGG <- enricher(gene = targets150_up, TERM2GENE = msig_go_bp[, c("gs_name", "gene_symbol")])
GO_enrich_down150_KEGG <- enricher(gene = targets150_down, TERM2GENE = msig_go_bp[, c("gs_name", "gene_symbol")])

p1 <- dotplot(GO_enrich_up150_KEGG, showCategory = 20) + 
  ggtitle("GO Enrichment KEGG for UPregulated targets for vesicles 150") + 
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 8)
  )

p2 <- dotplot(GO_enrich_down150_KEGG, showCategory = 20) + 
  ggtitle("GO Enrichment KEGG for DOWNregulated targets for vesicles 150") + 
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 8)
  )

p1 + p2

combined_plot <- p1 + p2

ggsave("./pictures/GO_enrichment_dotplot_KEGG_type150.tiff", plot = combined_plot, width = 16, height = 10, dpi = 300)

Анализ обогащения из базы CP:WIKIPATHWAYS

msig_go_bp <- msigdbr(species = "Homo sapiens", category = "C2", subcategory = "CP:WIKIPATHWAYS")

GO_enrich_up150_WIKI <- enricher(gene = targets150_up, TERM2GENE = msig_go_bp[, c("gs_name", "gene_symbol")])
GO_enrich_down150_WIKI <- enricher(gene = targets150_down, TERM2GENE = msig_go_bp[, c("gs_name", "gene_symbol")])

p1 <- dotplot(GO_enrich_up150_WIKI, showCategory = 20) + 
  ggtitle("GO Enrichment WIKIPATHWAYS for UPregulated targets for vesicles 150") + 
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 8)
  )

p2 <- dotplot(GO_enrich_down150_WIKI, showCategory = 20) + 
  ggtitle("GO Enrichment WIKIPATHWAYS for DOWNregulated targets for vesicles 150") + 
  theme(
    plot.title = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 8)
  )

p1 + p2
combined_plot <- p1 + p2

ggsave("./pictures/GO_enrichment_dotplot_WIKIPATHWAY_type150.tiff", plot = combined_plot, width = 16, height = 10, dpi = 300)

Только везикулы типа 16

coldata_16 <- coldata[coldata$type == 16, ]
rownames(coldata_16) <- coldata_16$sample
coldata_16
common_samples_16 <- intersect(colnames(counts), coldata_16$samples)

counts_16 <- counts[, c(counts$miRNA, common_samples)]  
counts_16 <- counts_16[, rownames(coldata_16)] #ранжирую по колонки в counts так же как и названия строк в coldata_150
head(counts_16)

Создаем DESeqDataSet из матрицы каунтов

dds_16 <- DESeqDataSetFromMatrix(countData = counts_16, 
                              colData = coldata_16, 
                              design = ~ 0 + patient + condition)
converting counts to integer mode
dds_16$condition <- relevel(dds_16$condition, ref = "before")
dds_16
class: DESeqDataSet 
dim: 913 6 
metadata(1): version
assays(1): counts
rownames(913): Hsa-Let-7-P1a_3p* Hsa-Let-7-P1a_5p/P2a1_5p/P2a2_5p ... Hsa-Mir-9851_3p Hsa-Mir-9851_5p*
rowData names(0):
colnames(6): 29.1p16_S39_R1_001 15.1p16_S33_R1_001 ... 15.7p16_S31_R1_001 17.7p16_S37_R1_001
colData names(4): sample condition type patient

Фильтрация

dim(dds_16)
[1] 913   6
smallestGroupSize <- 3
keep <- rowSums(counts(dds_16) >= 10) >= smallestGroupSize
dds_16 <- dds_16[keep,]
dim(dds_16)
[1] 219   6

Run Differential Expression Analysis for 150 type

dds_16 <- DESeq(dds_16, fitType = "parametric")
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
dds_16
class: DESeqDataSet 
dim: 219 6 
metadata(1): version
assays(4): counts mu H cooks
rownames(219): Hsa-Let-7-P1a_5p/P2a1_5p/P2a2_5p Hsa-Let-7-P1b_5p ... Hsa-Mir-96-P2_5p Hsa-Mir-96-P3_5p
rowData names(30): baseMean baseVar ... deviance maxCooks
colnames(6): 29.1p16_S39_R1_001 15.1p16_S33_R1_001 ... 15.7p16_S31_R1_001 17.7p16_S37_R1_001
colData names(5): sample condition type patient sizeFactor
plotDispEsts(dds_16)

res_16 <- results(dds_16, contrast=c("condition", "before", "after"))
res_16
log2 fold change (MLE): condition before vs after 
Wald test p-value: condition before vs after 
DataFrame with 219 rows and 6 columns
                                   baseMean log2FoldChange     lfcSE      stat    pvalue      padj
                                  <numeric>      <numeric> <numeric> <numeric> <numeric> <numeric>
Hsa-Let-7-P1a_5p/P2a1_5p/P2a2_5p 22993.7673      0.1901164  0.224536  0.846707 0.3971586  0.926559
Hsa-Let-7-P1b_5p                  2527.3696      0.4961225  0.280481  1.768830 0.0769222  0.575168
Hsa-Let-7-P1c_5p                   481.2148      0.5330152  0.461003  1.156209 0.2475958  0.860794
Hsa-Let-7-P2a1_3p*                  23.7282      0.4135740  1.966411  0.210319 0.8334185  0.954993
Hsa-Let-7-P2a3_5p                45006.2426     -0.0642043  0.238029 -0.269733 0.7873659  0.940503
...                                     ...            ...       ...       ...       ...       ...
Hsa-Mir-92-P2c_5p*                  25.3899      -0.754468  2.275831 -0.331513 0.7402570  0.931703
Hsa-Mir-95-P2_3p                   193.6126      -0.340069  0.662909 -0.512995 0.6079550  0.926559
Hsa-Mir-96-P1_5p                    14.4204      -4.216533  2.813830 -1.498503 0.1340025  0.764653
Hsa-Mir-96-P2_5p                  4249.9579      -0.537344  0.268063 -2.004539 0.0450123  0.487244
Hsa-Mir-96-P3_5p                   121.7907       0.504262  0.806362  0.625355 0.5317382  0.926559

MA plot

Фильтрация точек с низким средним экспрессированием (по baseMean). • Обычно отсекаются baseMean < 1. 2. Определение значимых генов (синие точки): • Используется критерий padj < 0.1 по умолчанию, а не < 0.05!

tiff("./pictures/PlotMA_standart_padj_0.05_type16.tiff", 
     width = 8, height = 6, units = "in", res = 300, bg = "white")
plotMA(res_16, alpha = 0.05, ylim = c(-8, 8)) 
dev.off()
null device 
          1 
plotMA(res_16, alpha = 0.05, ylim = c(-8, 8)) 

Кастомный MA plot по p-value

res_df <- res_16 %>%
  as.data.frame %>%
  mutate(color = case_when( 
    pvalue < 0.05 & !is.na(pvalue) & abs(log2FoldChange) > 1  ~ "blue",  # Значимые по p-value и диф экспрессированные
    TRUE ~ "gray70"
  ))

plt <- ggplot(res_df, aes(x = baseMean, y = log2FoldChange, color = color)) +
  geom_point(alpha = 0.7, size = 1) +
  geom_hline(yintercept = 0, linetype = "solid", color = "gray40", size = 1.5) +  # Добавляем линию
  scale_color_manual(values = c("gray70" = "gray70", "blue" = "blue")) +
  scale_x_log10(labels = scales::scientific) + 
  theme_minimal() +
  labs(x = "mean of normalized counts", y = "log fold change") +
  theme(legend.position = "none")

plt
ggsave("./pictures/PlotMA_castom_pvalue0.05_type16.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Значимые результаты

summary(results(dds_16, contrast=c("condition", "before", "after"), alpha=0.05))

out of 219 with nonzero total read count
adjusted p-value < 0.05
LFC > 0 (up)       : 0, 0%
LFC < 0 (down)     : 0, 0%
outliers [1]       : 0, 0%
low counts [2]     : 0, 0%
(mean count < 8)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results

Let’s arranged it by log2FoldChange:

order_indices <- order(-res_16$log2FoldChange)
res_16[order_indices, ]
log2 fold change (MLE): condition before vs after 
Wald test p-value: condition before vs after 
DataFrame with 219 rows and 6 columns
                    baseMean log2FoldChange     lfcSE      stat     pvalue      padj
                   <numeric>      <numeric> <numeric> <numeric>  <numeric> <numeric>
Hsa-Mir-185_5p       14.8310        5.85950   2.69426   2.17481 0.02964445  0.487244
Hsa-Mir-136_5p*      13.3826        5.83711   3.16104   1.84658 0.06480773  0.575168
Hsa-Mir-197_3p       45.5597        4.16951   1.59282   2.61769 0.00885267  0.357073
Hsa-Mir-101-P1_5p*   16.5354        4.08972   2.30004   1.77811 0.07538608  0.575168
Hsa-Mir-10-P3a_5p    37.9479        3.82488   1.69051   2.26256 0.02366292  0.487244
...                      ...            ...       ...       ...        ...       ...
Hsa-Mir-154-P17_3p   15.3729       -4.07531   2.60884  -1.56211  0.1182617  0.764653
Hsa-Mir-190-P1_5p    10.5910       -4.10593   3.44201  -1.19289  0.2329137  0.850135
Hsa-Mir-96-P1_5p     14.4204       -4.21653   2.81383  -1.49850  0.1340025  0.764653
Hsa-Mir-128-P1_5p*   15.3208       -4.49032   3.05489  -1.46988  0.1415942  0.764653
Hsa-Mir-197_5p*      17.0795       -5.15605   2.93332  -1.75775  0.0787901  0.575168

Visualisation for the first gene

plotCounts(dds_16, gene=which.max(res_16$log2FoldChange), intgroup="condition")

plotCounts(dds_16, gene=which.min(res_16$pvalue), intgroup="condition")

#plotCounts(dds, gene=rownames(res)[which.min(res$padj[which.max(res$log2FoldChange)])], intgroup="condition")

Volcano plot

plt <- EnhancedVolcano(res_16,
                lab = rownames(res_16),
                x = "log2FoldChange",
                y = "pvalue",
                pCutoff = 0.05,
                FCcutoff = 1,
                labSize = 3.0,
                boxedLabels = FALSE,
                col = c('black', '#CBD5E8', '#B3E2CD', '#FDCDAC'),
                colAlpha = 1,
                title = NULL,        
                subtitle = NULL) 

plt
ggsave("./pictures/Volcano plot_based_on_Pvalue_16type.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

rlog трансформация • Черные точки – стандартное отклонение отдельных генов. • Красная линия – сглаженный тренд зависимости SD от среднего значения экспрессии. • Если красная линия наклонена вверх → стандартное отклонение растёт с увеличением среднего (плохая нормализация). • Если красная линия примерно горизонтальна → нормализация сработала хорошо.

rlt_16 <- rlog(dds_16) 
meanSdPlot(assay(rlt_16))  #показывает, как изменяется стандартное отклонение в зависимости от среднего значения экспрессии.

pcaData <- plotPCA(rlt_16, intgroup=c("condition", "patient"), returnData = TRUE)
using ntop=500 top features by variance
percentVar <- round(100 * attr(pcaData, "percentVar"))

ggplot(pcaData, aes(PC1, PC2, shape = patient, color = condition)) +
  geom_point(size = 3) +
  xlab(paste0("PC1: ", percentVar[1], "%")) +
  ylab(paste0("PC2: ", percentVar[2], "%")) + 
  coord_fixed() +
  theme_bw() +
  scale_color_brewer(palette = "Set2")

assay(rlt_16) <- limma::removeBatchEffect(assay(rlt_16),
                                       batch = colData(dds_16)[,'patient'])

pcaData <- plotPCA(rlt_16, intgroup=c("condition", "patient"), returnData = TRUE)
using ntop=500 top features by variance
percentVar <- round(100 * attr(pcaData, "percentVar"))

plt <- ggplot(pcaData, aes(PC1, PC2, shape = patient, color = condition)) +
  geom_point(size = 3) +
  xlab(paste0("PC1: ", percentVar[1], "%")) +
  ylab(paste0("PC2: ", percentVar[2], "%")) + 
  coord_fixed() +
  theme_bw() +
  scale_color_brewer(palette = "Set2")

plt
ggsave("./pictures/PCA plot for type 16 after removing donor effect.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Plot a heatmap of the most expressed genes

res_sign_16 <- subset(res_16, pvalue < 0.05 & !is.na(pvalue) & abs(log2FoldChange) > 1.0)
res_sign_16 <- res_sign_16[order(res_sign_16$log2FoldChange, decreasing = TRUE), ]

sig_genes <- rownames(res_sign_16)  # Получаем имена генов, которые прошли фильтрацию

de_mat <- assay(rlt_16)[sig_genes, ] 
datamatrix <- t(scale(t(de_mat)))

annotation_col <- data.frame(condition = coldata_16$condition)
rownames(annotation_col) <- colnames(datamatrix)

annotation_colors <- list(
  condition = c("before" = "#FFCC00", "after" = "#3399FF")
)

plt <- pheatmap(datamatrix, 
         cluster_rows = TRUE, 
         show_rownames = TRUE, 
         cluster_cols = TRUE, 
         annotation_col = annotation_col,
         annotation_colors = annotation_colors,
         display_numbers = TRUE,
         legend = FALSE,
         fontsize = 15)  

plt

ggsave("./pictures/Heatmap of diff expressed genes_type16.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Plot of the distance between samples heatmap Расчет расстояний между образцами • Обычно используется евклидово расстояние (по умолчанию в DESeq2). • Оно вычисляется по нормализованным данным экспрессии (rlog() или vst()). • Чем меньше расстояние — тем более похожи образцы.

sampleDists_16 <- dist(t(assay(rlt_16)))
sampleDistMatrix_16 <- as.matrix(sampleDists_16)
rownames(sampleDistMatrix_16) <- paste(rlt_16$condition, rlt_16$patient, sep="_patient")
colnames(sampleDistMatrix_16) <- paste(rlt_16$condition, rlt_16$patient, sep="_patient")

plt <- pheatmap(sampleDistMatrix_16,
         clustering_distance_rows = "euclidean",
         clustering_distance_cols = "euclidean",
         fontsize = 12,
         legend = FALSE,
         display_numbers = TRUE,
         color = colors)

plt

ggsave("./pictures/Plot of the distance between samples_type16.tiff", plot = plt, width = 8, height = 6, dpi = 300, bg = "white")

Анализ обогащения для везикул типа 16

up_16 <- res_sign_16 %>% 
  as.data.frame() %>% 
  filter(log2FoldChange > 0)
down_16 <- res_sign_16 %>% 
  as.data.frame() %>% 
  filter(log2FoldChange < 0)
rownames(up_16)
[1] "Hsa-Mir-185_5p"     "Hsa-Mir-197_3p"     "Hsa-Mir-10-P3a_5p"  "Hsa-Mir-148-P3_5p*" "Hsa-Mir-30-P1a_3p*" "Hsa-Mir-150_3p*"   
[7] "Hsa-Mir-361_3p*"    "Hsa-Mir-10-P2b_5p"  "Hsa-Mir-126_3p*"   
rownames(down_16)
[1] "Hsa-Mir-340_5p"     "Hsa-Mir-181-P2c_5p" "Hsa-Mir-223_5p*"    "Hsa-Mir-30-P1c_3p*"

Переводим в miRBase • miRBase: https://www.mirbase.org/ • MirGeneDB: https://mirgenedb.org/

url <- "https://mirgenedb.org/browse/hsa"
page <- read_html(url)

Парсим таблицу

mir_table <- page %>%
  html_element("table") %>%
  html_table(fill = TRUE) 

mir_table <- mir_table[-c(1:3), c(1,2) ] 
colnames(mir_table) <- c("MirGeneDB_ID", "MiRBase_ID")
mir_table$MirGeneDB_ID <- sub(" V", "", mir_table$MirGeneDB_ID)

head(mir_table)
up_16_clean <- sub("_.*", "", row.names(up_16))
up_16_converted <- mir_table$MiRBase_ID[match(up_16_clean, mir_table$MirGeneDB_ID)]

#down_16_clean <- sub("_.*", "", row.names(down_16))
down_16_converted <- mir_table$MiRBase_ID[match(down_16_clean, mir_table$MirGeneDB_ID)]
up_16_converted
[1] "hsa-mir-185" "hsa-mir-197" NA            "hsa-mir-152" "hsa-mir-30d" "hsa-mir-150" "hsa-mir-361" "hsa-mir-99b" NA           
down_16_converted
[1] "hsa-mir-340"  "hsa-mir-181d" "hsa-mir-223"  NA            

Конвертация в MIMATID в итоге заменила NA вручную на самые близкие, но это такая себе практика

NA Hsa-Mir-10-P3a_5p есть два соответствия: Hsa-Mir-10-P1c = hsa-mir-10a Hsa-Mir-10-P3b = hsa-mir-125a

NA Hsa-Mir-126_3p* есть одно соответствие: Hsa-Mir-126-P2 = hsa-mir-126

NA Hsa-Mir-30-P1c_3p* есть три соответствия: Hsa-Mir-30-P1a = hsa-mir-30d Hsa-Mir-30-P1b = hsa-mir-30a Hsa-Mir-30-P1d = hsa-mir-30e

[1] “Hsa-Mir-185_5p” “Hsa-Mir-197_3p” “Hsa-Mir-10-P3a_5p” “Hsa-Mir-148-P3_5p” ”Hsa-Mir-30-P1a_3p” “Hsa-Mir-150_3p
[7] ”Hsa-Mir-361_3p
” “Hsa-Mir-10-P2b_5p” “Hsa-Mir-126_3p
[1] ”Hsa-Mir-340_5p” ”Hsa-Mir-181-P2c_5p” ”Hsa-Mir-223_5p
” “Hsa-Mir-30-P1c_3p*”

MI (MicroRNA Gene ID) — это идентификатор предшественника (precursor) miRNA MIMAT (Mature miRNA ID) — это идентификатор зрелой (mature) miRNA, которая функционирует в клетке

up_16_converted <-  c("hsa-mir-185-5p", "hsa-mir-197-3p", "hsa-mir-152-5p", "hsa-mir-30d-3p", "hsa-mir-150-3p", "hsa-mir-361-3p", "hsa-mir-99b-5p", "hsa-mir-126-3p")
down_16_converted <- c("hsa-mir-340-5p", "hsa-mir-181d-5p", "hsa-mir-223-5p")

# up_16_converted <-  c("hsa-mir-185", "hsa-mir-197", "hsa-mir-152", "hsa-mir-30d", "hsa-mir-150", "hsa-mir-361", "hsa-mir-99b", "hsa-mir-126")
# down_16_converted <- c("hsa-mir-340", "hsa-mir-181d", "hsa-mir-223") #но есть только MI, а не MIMAT

converted_mirna_up16 <- miRNAVersionConvert(up_16_converted)
converted_mirna_down16 <- miRNAVersionConvert(down_16_converted)
converted_mirna_up16
converted_mirna_down16

Запрос таргетов из базы multiMiR

targets16_up <- unique(get_multimir(org = "hsa", mirna = converted_mirna_up16$Accession, table = "predicted")@data$target_symbol)
targets16_up
targets16_down <- unique(get_multimir(org = "hsa", mirna = converted_mirna_down16$Accession, table = "predicted")@data$target_symbol)
targets16_down
LS0tCnRpdGxlOiAibWljcm8tUk5BIFZlc2ljbGVzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KKirQo9GB0YLQsNC90LDQstC70LjQstCw0LXQvCDQvdC10L7QsdGF0L7QtNC40LzRi9C1INCx0LjQsdC70LjQvtGC0LrQuCoqCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShERVNlcTIpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKbGlicmFyeShiaW9tYVJ0KQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShFbmhhbmNlZFZvbGNhbm8pCmxpYnJhcnkoR2Vub21pY1JhbmdlcykKbGlicmFyeShtc2lnZGJyKQpsaWJyYXJ5KG11bHRpTWlSKQpsaWJyYXJ5KG1pUkJhc2VDb252ZXJ0ZXIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeSh2c24pCmxpYnJhcnkocnZlc3QpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGRicGx5cikKYGBgCiMjINCY0LzQv9C+0YDRgtC40YDRg9C10Lwg0LTQsNC90L3Ri9C1CmBgYHtyfQpjb2xkYXRhIDwtIHJlYWRfdHN2KCJkYXRhL3BoZW5vdGFibGUudHN2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKcm93bmFtZXMoY29sZGF0YSkgPC0gY29sZGF0YSRzYW1wbGUKY29sZGF0YQpgYGAKCmBgYHtyfQpjb3VudHMgPC0gcmVhZC5jc3YoImRhdGEvbWlSLkNvdW50cy5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiLCIpCmNvdW50cyA8LSBjb2x1bW5fdG9fcm93bmFtZXMoY291bnRzLCB2YXIgPSAibWlSTkEiKQpoZWFkKGNvdW50cykKYGBgCmBgYHtyfQpjb2xuYW1lcyhjb3VudHMpIDwtIGdzdWIoIl5YIiwgIiIsIGNvbG5hbWVzKGNvdW50cykpCgpjb3VudHNfc2FtcGxlcyA8LSBjb2xuYW1lcyhjb3VudHMpCnBoZW5vdGFibGVfc2FtcGxlcyA8LSBjb2xkYXRhJHNhbXBsZQpjb21tb25fc2FtcGxlcyA8LSBpbnRlcnNlY3QoY291bnRzX3NhbXBsZXMsIHBoZW5vdGFibGVfc2FtcGxlcykKCmNvdW50cyA8LSBjb3VudHNbLCBjKGNvdW50cyRtaVJOQSwgY29tbW9uX3NhbXBsZXMpXSAgCmNvdW50cyA8LSBjb3VudHNbLCByb3duYW1lcyhjb2xkYXRhKV0gI9GA0LDQvdC20LjRgNGD0Y4g0L/QviDQutC+0LvQvtC90LrQuCDQsiBjb3VudHMg0YLQsNC6INC20LUg0LrQsNC6INC4INC90LDQt9Cy0LDQvdC40Y8g0YHRgtGA0L7QuiDQsiBjb2xkYXRhCmhlYWQoY291bnRzKQpgYGAKYGBge3J9CmFubm8gPC0gcmVhZC5jc3YoImRhdGEvYW5ub3RhdGlvbi5yZXBvcnQuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiKQphbm5vJFNhbXBsZS5uYW1lLnMuIDwtIGdzdWIoIi0iLCAiLiIsIGFubm8kU2FtcGxlLm5hbWUucy4pCmFubm8gPC0gYW5ub1ssIC1jKDI6NSwgNywgMTUpXQoKCmNvbW1vbl9zYW1wbGVzIDwtIGludGVyc2VjdChhbm5vJFNhbXBsZS5uYW1lLnMuLCBjb2xkYXRhJHNhbXBsZSkKCmFubm8gPC0gYW5ub1thbm5vJFNhbXBsZS5uYW1lLnMuICVpbiUgY29tbW9uX3NhbXBsZXMsIF0KYW5ubyA8LSBhbm5vW21hdGNoKHJvd25hbWVzKGNvbGRhdGEpLCBhbm5vJFNhbXBsZS5uYW1lLnMuKSwgXSAj0YDQsNC90LbQuNGA0YPRjiDQv9C+INC60L7Qu9C+0L3QutC4INCyIGNvdW50cyDRgtCw0Log0LbQtSDQutCw0Log0Lgg0L3QsNC30LLQsNC90LjRjyDRgdGC0YDQvtC6INCyIGNvbGRhdGEKYW5ubwpgYGAKIyMg0JLQtdGB0Ywg0LTQsNGC0LDRgdC10YIKYGBge3J9CmFubm9fbG9uZyA8LSBhbm5vICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLVNhbXBsZS5uYW1lLnMuLCBuYW1lc190byA9ICJSTkFfVHlwZSIsIHZhbHVlc190byA9ICJDb3VudCIpCgpwbHQgPC0gZ2dwbG90KGFubm9fbG9uZywgYWVzKHggPSBTYW1wbGUubmFtZS5zLiwgeSA9IENvdW50LCBmaWxsID0gUk5BX1R5cGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoeCA9ICJTYW1wbGUiLCB5ID0gIlJlYWQgQ291bnQiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MyIpICAjINCa0YDQsNGB0LjQstGL0LUg0YbQstC10YLQsAoKcHJpbnQocGx0KQpnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX2JhcnBsb3RfYWxsZGF0YXNldF9ub19ub3JtYWxpc2VkLnRpZmYiLCBwbG90ID0gcGx0LCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCwgIGJnID0gIndoaXRlIikKYGBgCgpgYGB7cn0KYW5ub19sb25nIDwtIGFubm8gJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShhY3Jvc3MoLVNhbXBsZS5uYW1lLnMuLCB+IC4gLyBzdW0oY19hY3Jvc3MoLVNhbXBsZS5uYW1lLnMuKSkpKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLVNhbXBsZS5uYW1lLnMuLCBuYW1lc190byA9ICJSTkFfVHlwZSIsIHZhbHVlc190byA9ICJQcm9wb3J0aW9uIikKCnBsdCA8LSBnZ3Bsb3QoYW5ub19sb25nLCBhZXMoeCA9IFNhbXBsZS5uYW1lLnMuLCB5ID0gUHJvcG9ydGlvbiwgZmlsbCA9IFJOQV9UeXBlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHggPSAiU2FtcGxlIiwgeSA9ICJQcm9wb3J0aW9uIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKQoKcGx0Cmdnc2F2ZSgiLi9waWN0dXJlc190cmFuc3BsL3RyYW5zcGxfYmFycGxvdF9hbGxkYXRhc2V0X25vcm1hbGlzZWQudGlmZiIsIHBsb3QgPSBwbHQsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwLCBiZyA9ICJ3aGl0ZSIpCmBgYApgYGB7cn0KY29sZGF0YSRjb25kaXRpb24gPC0gcmVsZXZlbChmYWN0b3IoY29sZGF0YSRjb25kaXRpb24pLCByZWYgPSAibm9fY29tcGxpY2F0aW9ucyIpCm1vZGVsTWF0cml4IDwtIG1vZGVsLm1hdHJpeCh+Y29uZGl0aW9uLCBjb2xkYXRhKQptb2RlbE1hdHJpeApxcihtb2RlbE1hdHJpeCkkcmFuayAgIyDRgNCw0L3QsyDQvNCw0YLRgNC40YbRiwpuY29sKG1vZGVsTWF0cml4KSAKYGBgCgoKYGBge3J9CmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGNvdW50cywgY29sRGF0YSA9IGNvbGRhdGEsIGRlc2lnbiA9IH4gY29uZGl0aW9uKQpkZHMkY29uZGl0aW9uIDwtIHJlbGV2ZWwoZGRzJGNvbmRpdGlvbiwgcmVmID0gIm5vX2NvbXBsaWNhdGlvbnMiKQpkZHMKYGBgCmBgYHtyfQpkaW0oZGRzKQpzbWFsbGVzdEdyb3VwU2l6ZSA8LSAxNQprZWVwIDwtIHJvd1N1bXMoY291bnRzKGRkcykgPj0gMTApID49IHNtYWxsZXN0R3JvdXBTaXplCmRkcyA8LSBkZHNba2VlcCxdCmRpbShkZHMpCmBgYAojIyMgIFJ1biBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpcyAjIyMKYGBge3J9CmRkcyA8LSBERVNlcShkZHMsIGZpdFR5cGUgPSAicGFyYW1ldHJpYyIpCmRkcwpgYGAKCmBgYHtyfQpwbG90RGlzcEVzdHMoZGRzKQpgYGAKYGBge3J9CnJhd19jb3VudHMgPC0gY291bnRzKGRkcywgbm9ybWFsaXplZCA9IEZBTFNFKQpub3JtYWxpemVkX2NvdW50cyA8LSBjb3VudHMoZGRzLCBub3JtYWxpemVkID0gVFJVRSkKCmRmIDwtIGRhdGEuZnJhbWUoCiAgU2FtcGxlID0gcmVwKGNvbG5hbWVzKGRkcyksIDIpLAogIENvdW50cyA9IGMoY29sU3VtcyhyYXdfY291bnRzKSwgY29sU3Vtcyhub3JtYWxpemVkX2NvdW50cykpLAogIFR5cGUgPSByZXAoYygiUmF3IiwgIk5vcm1hbGl6ZWQiKSwgZWFjaCA9IG5jb2woZGRzKSkKKQoKcGx0IDwtIGdncGxvdChkZiwgYWVzKHggPSBTYW1wbGUsIHkgPSBDb3VudHMsIGZpbGwgPSBUeXBlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiQ291bnRzIGJlZm9yZSBhbmQgYWZ0ZXIgbm9ybWFsaXphdGlvbiIsIHggPSAiU2FtcGxlIiwgeSA9ICJUb3RhbCBDb3VudHMiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKCnBsdApnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX0NvdW50cyBiZWZvcmUgYW5kIGFmdGVyIG5vcm1hbGl6YXRpb24udGlmZiIsIHBsb3QgPSBwbHQsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwLCBiZyA9ICJ3aGl0ZSIpCmBgYApybG9nINGC0YDQsNC90YHRhNC+0YDQvNCw0YbQuNGPCmBgYHtyfQpybHQgPC0gcmxvZyhkZHMpICAjcmxvZyBUcmFuc2Zvcm1hdGlvbgptZWFuU2RQbG90KGFzc2F5KHJsdCkpIAoKYGBgCmBgYHtyfQp2c2QgPC0gdmFyaWFuY2VTdGFiaWxpemluZ1RyYW5zZm9ybWF0aW9uKGRkcywgYmxpbmQ9RkFMU0UpIAptZWFuU2RQbG90KGFzc2F5KHZzZCkpICPQv9C+0LrQsNC30YvQstCw0LXRgiwg0LrQsNC6INC40LfQvNC10L3Rj9C10YLRgdGPINGB0YLQsNC90LTQsNGA0YLQvdC+0LUg0L7RgtC60LvQvtC90LXQvdC40LUg0LIg0LfQsNCy0LjRgdC40LzQvtGB0YLQuCDQvtGCINGB0YDQtdC00L3QtdCz0L4g0LfQvdCw0YfQtdC90LjRjyDRjdC60YHQv9GA0LXRgdGB0LjQuApgYGAKCioqIFBDQSBwbG90ICoqCmBgYHtyfQpwY2FEYXRhIDwtIHBsb3RQQ0Eocmx0LCBpbnRncm91cD1jKCJjb25kaXRpb24iKSwgcmV0dXJuRGF0YSA9IFRSVUUpCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoKcGNhRGF0YSRzYW1wbGUgPC0gZ3N1YigiXy4qIiwgIiIsIGNvbGRhdGEkc2FtcGxlKQoKCnBsdCA8LSBnZ3Bsb3QocGNhRGF0YSwgYWVzKFBDMSwgUEMyLCBjb2xvciA9IGNvbmRpdGlvbikpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXNhbXBsZSksIHNpemU9Mywgdmp1c3Q9MS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHhsYWIocGFzdGUwKCJQQzE6ICIsIHBlcmNlbnRWYXJbMV0sICIlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLCBwZXJjZW50VmFyWzJdLCAiJSIpKSArIAogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiKQpwbHQKZ2dzYXZlKCIuL3BpY3R1cmVzX3RyYW5zcGwvdHJhbnNwbF9QQ0EgcGxvdC50aWZmIiwgcGxvdCA9IHBsdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKYGBgCgoqKlBsb3QgYSBoZWF0bWFwIG9mIDUwIG1vc3QgZXhwcmVzc2VkIGdlbmVzKioK0K3RgtC+0YIgaGVhdG1hcCDQvtGC0YDQsNC20LDQtdGCINGD0YDQvtCy0L3QuCDRjdC60YHQv9GA0LXRgdGB0LjQuCDQs9C10L3QvtCyLCDQsCDQvdC1INGA0LDQt9C90LjRhtGDINC80LXQttC00YMg0LPRgNGD0L/Qv9Cw0LzQuC4K0KbQstC10YLQsCDQvdC1INC+0LfQvdCw0YfQsNGO0YIgdXAtINC40LvQuCBkb3duLdGA0LXQs9GD0LvRj9GG0LjRjiDQsiDRgdGA0LDQstC90LXQvdC40Lgg0YEg0LrQvtC90YLRgNC+0LvRjNC90L7QuSDQs9GA0YPQv9C/0L7QuSwg0L/QvtGC0L7QvNGDINGH0YLQviBoZWF0bWFwINC/0L7QutCw0LfRi9Cy0LDQtdGCINCw0LHRgdC+0LvRjtGC0L3Ri9C1INC30L3QsNGH0LXQvdC40Y8g0Y3QutGB0L/RgNC10YHRgdC40LgsINCwINC90LUgZm9sZCBjaGFuZ2UhCmBgYHtyfQpzZWxlY3QgPC0gb3JkZXIocm93TWVhbnMoY291bnRzKGRkcyxub3JtYWxpemVkPVRSVUUpKSwKICAgICAgICAgICAgICAgIGRlY3JlYXNpbmc9VFJVRSlbMTo1MF0KZGYgPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKGRkcykkY29uZGl0aW9uKQpjb2xuYW1lcyhkZikgPC0gImNvbmRpdGlvbiIKcm93bmFtZXMoZGYpIDwtIGNvbG5hbWVzKGNvdW50cyhkZHMpKQpwbHQgPC0gcGhlYXRtYXAoYXNzYXkocmx0KVtzZWxlY3QsXSwgCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIAogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgCiAgICAgICAgIGNsdXN0ZXJfY29scyA9IFRSVUUsIAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGRmLAogICAgICAgICBmb250c2l6ZV9yb3cgPSA2KSAKCnBsdApnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX1Bsb3QgYSBoZWF0bWFwIG9mIDUwIG1vc3QgZXhwcmVzc2VkIGdlbmVzLnRpZmYiLCBwbG90ID0gcGx0LCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCwgYmcgPSAid2hpdGUiKQpgYGAKKipQbG90IG9mIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHNhbXBsZXMgaGVhdG1hcCoqCtCg0LDRgdGH0LXRgiDRgNCw0YHRgdGC0L7Rj9C90LjQuSDQvNC10LbQtNGDINC+0LHRgNCw0LfRhtCw0LzQuAoJ4oCiCdCe0LHRi9GH0L3QviDQuNGB0L/QvtC70YzQt9GD0LXRgtGB0Y8g0LXQstC60LvQuNC00L7QstC+INGA0LDRgdGB0YLQvtGP0L3QuNC1ICjQv9C+INGD0LzQvtC70YfQsNC90LjRjiDQsiBERVNlcTIpLgoJ4oCiCdCe0L3QviDQstGL0YfQuNGB0LvRj9C10YLRgdGPINC/0L4g0L3QvtGA0LzQsNC70LjQt9C+0LLQsNC90L3Ri9C8INC00LDQvdC90YvQvCDRjdC60YHQv9GA0LXRgdGB0LjQuCAocmxvZygpINC40LvQuCB2c3QoKSkuCgnigKIJ0KfQtdC8INC80LXQvdGM0YjQtSDRgNCw0YHRgdGC0L7Rj9C90LjQtSDigJQg0YLQtdC8INCx0L7Qu9C10LUg0L/QvtGF0L7QttC4INC+0LHRgNCw0LfRhtGLLgpgYGB7cn0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHJsdCkpKQpzYW1wbGVEaXN0TWF0cml4IDwtIGFzLm1hdHJpeChzYW1wbGVEaXN0cykKcm93bmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gcGFzdGUocmx0JGNvbmRpdGlvbikKY29sbmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gcGFzdGUocmx0JGNvbmRpdGlvbikKY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQoKcGx0IDwtIHBoZWF0bWFwKHNhbXBsZURpc3RNYXRyaXgsCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJldWNsaWRlYW4iLAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiZXVjbGlkZWFuIiwKICAgICAgICAgY29sb3IgPSBjb2xvcnMpCgpwbHQKZ2dzYXZlKCIuL3BpY3R1cmVzX3RyYW5zcGwvdHJhbnNwbF9QbG90IG9mIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHNhbXBsZXMgaGVhdG1hcC50aWZmIiwgcGxvdCA9IHBsdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKYGBgCmBgYHtyfQpyZXNfaHVtb3JhbCA8LSByZXN1bHRzKGRkcywgY29udHJhc3Q9YygiY29uZGl0aW9uIiwgIm5vX2NvbXBsaWNhdGlvbnMiLCAiaHVtb3JhbCIpKQpyZXNfaHVtb3JhbApgYGAKKipNQSBwbG90KioK0KTQuNC70YzRgtGA0LDRhtC40Y8g0YLQvtGH0LXQuiDRgSDQvdC40LfQutC40Lwg0YHRgNC10LTQvdC40Lwg0Y3QutGB0L/RgNC10YHRgdC40YDQvtCy0LDQvdC40LXQvCAo0L/QviBiYXNlTWVhbikuCgnigKIJ0J7QsdGL0YfQvdC+INC+0YLRgdC10LrQsNGO0YLRgdGPIGJhc2VNZWFuIDwgMS4KCTIuCdCe0L/RgNC10LTQtdC70LXQvdC40LUg0LfQvdCw0YfQuNC80YvRhSDQs9C10L3QvtCyICjRgdC40L3QuNC1INGC0L7Rh9C60LgpOgoJ4oCiCdCY0YHQv9C+0LvRjNC30YPQtdGC0YHRjyDQutGA0LjRgtC10YDQuNC5IHBhZGogPCAwLjEg0L/QviDRg9C80L7Qu9GH0LDQvdC40Y4sINCwINC90LUgPCAwLjA1ISAKYGBge3J9CnRpZmYoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX1Bsb3RNQV9zdGFuZGFydF9wYWRqXzAuMDVfaHVtb3JhbC50aWZmIiwgCiAgICAgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCB1bml0cyA9ICJpbiIsIHJlcyA9IDMwMCwgYmcgPSAid2hpdGUiKQpwbG90TUEocmVzX2h1bW9yYWwsIGFscGhhID0gMC4wNSwgeWxpbSA9IGMoLTgsIDgpKSAKZGV2Lm9mZigpCnBsb3RNQShyZXNfaHVtb3JhbCwgYWxwaGEgPSAwLjA1LCB5bGltID0gYygtOCwgOCkpIApgYGAKKirQmtCw0YHRgtC+0LzQvdGL0LkgTUEgcGxvdCoqCmBgYHtyfQpyZXNfZGYgPC0gcmVzX2h1bW9yYWwgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIG11dGF0ZShjb2xvciA9IGNhc2Vfd2hlbiggCiAgICBwYWRqIDwgMC4wNSAgfiAicGFkaiA8IDAuMDUiLCAgIAogICAgcHZhbHVlIDwgMC4wNSAgfiAicHZhbHVlIDwgMC4wNSIsIAogICAgVFJVRSB+ICJBbGwiCiAgKSkKCnBsdCA8LSBnZ3Bsb3QocmVzX2RmLCBhZXMoeCA9IGJhc2VNZWFuLCB5ID0gbG9nMkZvbGRDaGFuZ2UsIGNvbG9yID0gY29sb3IpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgc2l6ZSA9IDEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJzb2xpZCIsIGNvbG9yID0gImdyYXk0MCIsIHNpemUgPSAxLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQWxsIiA9ICJncmF5NzAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHZhbHVlIDwgMC4wNSIgPSAiYmx1ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYWRqIDwgMC4wNSIgPSAicmVkIikpICsKICBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6c2NpZW50aWZpYykgKyAKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoeCA9ICJtZWFuIG9mIG5vcm1hbGl6ZWQgY291bnRzIiwgCiAgICAgICB5ID0gImxvZyBmb2xkIGNoYW5nZSIsIAogICAgICAgY29sb3IgPSBOVUxMKQoKcGx0Cmdnc2F2ZSgiLi9waWN0dXJlc190cmFuc3BsL3RyYW5zcGxf0KF1c3RvbSBNQXBsb3RfaHVtb3JhbC50aWZmIiwgcGxvdCA9IHBsdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKYGBgCgoqKtCX0L3QsNGH0LjQvNGL0LUg0YDQtdC30YPQu9GM0YLQsNGC0YsqKgoKNTgg0LPQtdC90L7QsiAoMjAlKSDQuNC80LXRjtGCINC90LjQt9C60LjQtSDRg9GA0L7QstC90Lgg0Y3QutGB0L/RgNC10YHRgdC40Lgg0Lgg0YTQuNC70YzRgtGA0YPRjtGC0YHRjyDQuNC3INCw0L3QsNC70LjQt9CwLgppbmRlcGVuZGVudCBmaWx0ZXJpbmcg4oCUINC/0YDQvtGG0LXQtNGD0YDQsCwg0LrQvtGC0L7RgNCw0Y8g0LjRgdC60LvRjtGH0LDQtdGCINCz0LXQvdGLINGBINC90LjQt9C60LjQvNC4INC30L3QsNGH0LXQvdC40Y/QvNC4INC00LvRjyDRg9Cy0LXQu9C40YfQtdC90LjRjyDRgdGC0LDRgtC40YHRgtC40YfQtdGB0LrQvtC5INC80L7RidC90L7RgdGC0LguCmBgYHtyfQpzaWducmVzX2h1bW9yYWwgPC0gcmVzdWx0cyhkZHMsIGNvbnRyYXN0PWMoImNvbmRpdGlvbiIsICJub19jb21wbGljYXRpb25zIiwgImh1bW9yYWwiKSwgYWxwaGE9MC4wNSkgCnN1bW1hcnkoc2lnbnJlc19odW1vcmFsKQpgYGAKTGV0J3MgYXJyYW5nZWQgaXQgYnkgbG9nMkZvbGRDaGFuZ2U6CmBgYHtyfQpvcmRlcl9pbmRpY2VzIDwtIG9yZGVyKC1yZXNfaHVtb3JhbCRsb2cyRm9sZENoYW5nZSkKcmVzX2h1bW9yYWxbb3JkZXJfaW5kaWNlcywgXQpgYGAKVmlzdWFsaXNhdGlvbiBmb3IgdGhlIGZpcnN0IGdlbmUKYGBge3J9CiNwbG90Q291bnRzKGRkcywgZ2VuZT13aGljaC5tYXgocmVzX2h1bW9yYWwkbG9nMkZvbGRDaGFuZ2UpLCBpbnRncm91cD0iY29uZGl0aW9uIikKcGxvdENvdW50cyhkZHMsIGdlbmU9d2hpY2gubWluKHJlc19odW1vcmFsJHBhZGopLCBpbnRncm91cD0iY29uZGl0aW9uIikKI3Bsb3RDb3VudHMoZGRzLCBnZW5lPXJvd25hbWVzKHJlcylbd2hpY2gubWluKHJlcyRwYWRqW3doaWNoLm1heChyZXMkbG9nMkZvbGRDaGFuZ2UpXSldLCBpbnRncm91cD0iY29uZGl0aW9uIikKYGBgCioqVm9sY2FubyBwbG90KioKYGBge3J9CnBsdCA8LSBFbmhhbmNlZFZvbGNhbm8ocmVzX2h1bW9yYWwsCiAgICAgICAgICAgICAgICBsYWIgPSByb3duYW1lcyhyZXNfaHVtb3JhbCksCiAgICAgICAgICAgICAgICB4ID0gImxvZzJGb2xkQ2hhbmdlIiwKICAgICAgICAgICAgICAgIHkgPSAicGFkaiIsCiAgICAgICAgICAgICAgICBwQ3V0b2ZmID0gMC4wNSwKICAgICAgICAgICAgICAgIEZDY3V0b2ZmID0gMSwKICAgICAgICAgICAgICAgIGxhYlNpemUgPSAzLjAsCiAgICAgICAgICAgICAgICBib3hlZExhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICAgY29sID0gYygnYmxhY2snLCAnI0NCRDVFOCcsICcjQjNFMkNEJywgJyNGRENEQUMnKSwKICAgICAgICAgICAgICAgIGNvbEFscGhhID0gMSwKICAgICAgICAgICAgICAgIHRpdGxlID0gTlVMTCwgICAgICAgIAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBOVUxMKSAKCnBsdApnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX1ZvbGNhbm9QbG90X2h1bW9yYWwudGlmZiIsIHBsb3QgPSBwbHQsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwLCBiZyA9ICJ3aGl0ZSIpCmBgYApgYGB7cn0KY29sZGF0YV9maWx0ZXJlZCA8LSBjb2xkYXRhW2NvbGRhdGEkY29uZGl0aW9uICVpbiUgYygiaHVtb3JhbCIsICJub19jb21wbGljYXRpb25zIiksIF0KY29sZGF0YV9maWx0ZXJlZApgYGAKCioqUGxvdCBhIGhlYXRtYXAgb2YgZGlmZiBleHByZXNzZWQgZ2VuZXMqKgpgYGB7cn0KcmVzX3NpZ25faHVtb3JhbCA8LSBzdWJzZXQocmVzX2h1bW9yYWwsIHBhZGogPCAwLjA1ICYgIWlzLm5hKHBhZGopICYgYWJzKGxvZzJGb2xkQ2hhbmdlKSA+IDEuMCkKcmVzX3NpZ25faHVtb3JhbCA8LSByZXNfc2lnbl9odW1vcmFsW29yZGVyKHJlc19zaWduX2h1bW9yYWwkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQoKc2lnX2dlbmVzIDwtIHJvd25hbWVzKHJlc19zaWduX2h1bW9yYWwpICAKCmRlX21hdCA8LSBhc3NheShybHQpW3NpZ19nZW5lcywgXSAKCmRlX21hdF9maWx0ZXJlZCA8LSBkZV9tYXRbLCBjb2xkYXRhX2ZpbHRlcmVkJHNhbXBsZV0KI2RhdGFtYXRyaXggPC0gdChzY2FsZSh0KGRlX21hdF9maWx0ZXJlZCkpKQpkYXRhbWF0cml4IDwtIGRlX21hdF9maWx0ZXJlZAoKYW5ub3RhdGlvbl9jb2wgPC0gZGF0YS5mcmFtZShjb25kaXRpb24gPSBjb2xkYXRhX2ZpbHRlcmVkJGNvbmRpdGlvbikKcm93bmFtZXMoYW5ub3RhdGlvbl9jb2wpIDwtIGNvbG5hbWVzKGRhdGFtYXRyaXgpCgphbm5vdGF0aW9uX2NvbG9ycyA8LSBsaXN0KAogIGNvbmRpdGlvbiA9IGMoIm5vX2NvbXBsaWNhdGlvbnMiID0gIiNGRkNDMDAiLCAiaHVtb3JhbCIgPSAiIzMzOTlGRiIpKQoKcGx0IDwtIHBoZWF0bWFwKGRhdGFtYXRyaXgsCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIAogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgCiAgICAgICAgIGNsdXN0ZXJfY29scyA9IFRSVUUsIAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGFubm90YXRpb25fY29sLAogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm90YXRpb25fY29sb3JzLAogICAgICAgICBkaXNwbGF5X251bWJlcnMgPSBGQUxTRSwKICAgICAgICAgbGVnZW5kID0gVFJVRSwKICAgICAgICAgZm9udHNpemUgPSAxNSkgIAoKZ2dzYXZlKCIuL3BpY3R1cmVzX3RyYW5zcGwvdHJhbnNwbF9IZWF0bWFwIG9mIGRpZmYgZXhwcmVzc2VkIGdlbmVzX2h1bW9yYWwudGlmZiIsIHBsb3QgPSBwbHQsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKCiMjIyDQkNC90LDQu9C40Lcg0L7QsdC+0LPQsNGJ0LXQvdC40Y8g0LPRg9C80L7RgNCw0LvRjNC90YvQuSDQvtGC0LLQtdGCICMjIwpgYGB7cn0KdXBfaHVtb3JhbCA8LSByZXNfc2lnbl9odW1vcmFsICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogIGZpbHRlcihsb2cyRm9sZENoYW5nZSA+IDApCmRvd25faHVtb3JhbCA8LSByZXNfc2lnbl9odW1vcmFsICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogIGZpbHRlcihsb2cyRm9sZENoYW5nZSA8IDApCnJvd25hbWVzKHVwX2h1bW9yYWwpCnJvd25hbWVzKGRvd25faHVtb3JhbCkKYGBgCgkK0J/QtdGA0LXQstC+0LTQuNC8INCyIG1pUkJhc2UKCeKAogltaVJCYXNlOiBodHRwczovL3d3dy5taXJiYXNlLm9yZy8KCeKAoglNaXJHZW5lREI6IGh0dHBzOi8vbWlyZ2VuZWRiLm9yZy8KYGBge3J9CnVybCA8LSAiaHR0cHM6Ly9taXJnZW5lZGIub3JnL2Jyb3dzZS9oc2EiCnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKYGBgCgrQn9Cw0YDRgdC40Lwg0YLQsNCx0LvQuNGG0YMKYGBge3J9Cm1pcl90YWJsZSA8LSBwYWdlICU+JQogIGh0bWxfZWxlbWVudCgidGFibGUiKSAlPiUKICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKSAKCm1pcl90YWJsZSA8LSBtaXJfdGFibGVbLWMoMTozKSwgYygxLDIpIF0gCmNvbG5hbWVzKG1pcl90YWJsZSkgPC0gYygiTWlyR2VuZURCX0lEIiwgIk1pUkJhc2VfSUQiKQptaXJfdGFibGUkTWlyR2VuZURCX0lEIDwtIHN1YigiIFYiLCAiIiwgbWlyX3RhYmxlJE1pckdlbmVEQl9JRCkKCmhlYWQobWlyX3RhYmxlKQpgYGAKYGBge3J9CmRvd25faHVtb3JhbF9jbGVhbiA8LSBzdWIoIl8uKiIsICIiLCByb3cubmFtZXMoZG93bl9odW1vcmFsKSkKZG93bl9odW1vcmFsX2NvbnZlcnRlZCA8LSBtaXJfdGFibGUkTWlSQmFzZV9JRFttYXRjaChkb3duX2h1bW9yYWxfY2xlYW4sIG1pcl90YWJsZSRNaXJHZW5lREJfSUQpXQpkb3duX2h1bW9yYWxfY29udmVydGVkCmBgYAoKKirQmtC+0L3QstC10YDRgtCw0YbQuNGPINCyIE1JTUFUSUQqKiAKTkEg0LHQtdC3INGB0L7QvtGC0LLQtdGC0YHRgtCy0LjRjyDRg9C00LDQu9C40LvQsCDQuNC3INCw0L3QsNC70LjQt9CwCgpOQSBIc2EtTWlyLTE0OC1QMl8zcCAg0LXRgdGC0Ywg0YLRgNC4INC/0L7RhdC+0LbQuNGFINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjRjzoKSHNhLU1pci0xNDgtUDEJaHNhLW1pci0xNDhhCQkJCQkJCQkJCQkJCQkJCQkJCQpIc2EtTWlyLTE0OC1QMwloc2EtbWlyLTE1MgkJCQkJCQkJCQkJCQkJCQkJCQkJCkhzYS1NaXItMTQ4LVA0CWhzYS1taXItMTQ4YgoKTkEgSHNhLU1pci0xMC1QMmFfNXAg0LXRgdGC0Ywg0YLRgNC4INC/0L7RhdC+0LbQuNGFINGB0L7QvtGC0LLQtdGC0YHRgtCy0LjQtTogCkhzYS1NaXItMTAtUDJiCWhzYS1taXItOTliCQkJCQkJCQkJCQkJCQkJCkhzYS1NaXItMTAtUDJjCWhzYS1taXItOTlhCQkJCQkJCQkJCQkJCQkJCQkJCkhzYS1NaXItMTAtUDJkCWhzYS1taXItMTAwCgpbMV0gIkhzYS1NaXItMTQ2LVAxXzVwIiAgICJIc2EtTWlyLTQyNV8zcCoiICAgICAiSHNhLU1pci0xNDgtUDJfM3AiICAgIkhzYS1NaXItMTAtUDNjXzVwIiAgICJIc2EtTWlyLTEwLVAyYV81cCIgIApbNl0gIkhzYS1NaXItMTkzLVAxYl81cCoiICJIc2EtTWlyLTE5My1QMWJfM3AiIAoKYGBge3J9Cm1pcm5hX25hbWVzX2Rvd24gPC0gYygiaHNhLW1pUi0xNDZiLTVwIiwgImhzYS1taVItNDI1LTNwIiwgImhzYS1taVItMTI1Yi01cCIsICJoc2EtbWlSLTE5M2EtNXAiLCAiaHNhLW1pUi0xOTNhLTNwIikKY29udmVydGVkX21pcm5hX2Rvd24gPC0gbWlSTkFWZXJzaW9uQ29udmVydChtaXJuYV9uYW1lc19kb3duKQpjb252ZXJ0ZWRfbWlybmFfZG93bgpgYGAKKirQl9Cw0L/RgNC+0YEg0YLQsNGA0LPQtdGC0L7QsiDQuNC3INCx0LDQt9GLIG11bHRpTWlSKiogCmBgYHtyfQp0YXJnZXRzX2h1bW9yYWxfZG93biA8LSB1bmlxdWUoZ2V0X211bHRpbWlyKG9yZyA9ICJoc2EiLCBtaXJuYSA9IGNvbnZlcnRlZF9taXJuYV9kb3duJEFjY2Vzc2lvbiwgdGFibGUgPSAidmFsaWRhdGVkIilAZGF0YSR0YXJnZXRfc3ltYm9sKQojd3JpdGVMaW5lcyh0YXJnZXRzX2Rvd24sICJ0YXJnZXRzX2Rvd24xNTBfbGlzdC50eHQiKQpgYGAKKirQkNC90LDQu9C40Lcg0L7QsdC+0LPQsNGJ0LXQvdC40Y8g0LjQtyDQsdCw0LfRiyDQsdC40L7Qu9C+0LPQuNGH0LXRgdC60LjRhSDQv9GA0L7RhtC10YHRgdC+0LIqKgpgYGB7cn0KI21zaWdfZ29fYnAgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIsIGNhdGVnb3J5ID0gIkM1Iiwgc3ViY2F0ZWdvcnkgPSAiR086QlAiKQojIHRhcmdldHNfZG93biA8LSByZWFkTGluZXMoInRhcmdldHNfZG93bjE1MF9saXN0LnR4dCIpCiMgdGFyZ2V0c191cCA8LSByZWFkTGluZXMoInRhcmdldHNfdXAxNTBfbGlzdC50eHQiKQoKR09fZW5yaWNoX2Rvd25faHVtb3JhbF9icCA8LSBlbnJpY2hHTygKICBnZW5lICAgICAgICAgID0gdGFyZ2V0c19odW1vcmFsX2Rvd24sICAKICBPcmdEYiAgICAgICAgID0gb3JnLkhzLmVnLmRiLAogIGtleVR5cGUgICAgICAgPSAiU1lNQk9MIiwKICBvbnQgICAgICAgICAgID0gIkJQIiwgCiAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgcXZhbHVlQ3V0b2ZmICA9IDAuMDUKKQpgYGAKCioq0JLQuNC30YPQsNC70LjQt9Cw0YbQuNGPKioKYGBge3J9CgpwMSA8LSBkb3RwbG90KEdPX2VucmljaF9kb3duX2h1bW9yYWxfYnAsIHNob3dDYXRlZ29yeSA9IDIwKSArIAogIGdndGl0bGUoIkdPIEVucmljaG1lbnQgZm9yIERPV05yZWd1bGF0ZWQgdGFyZ2V0cyIpICsgCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpCiAgKQoKcDEKCmdnc2F2ZSgiLi9waWN0dXJlc190cmFuc3BsL3RyYW5zcGxfR09fZW5yaWNobWVudF9kb3RwbG90X2Rvd25faHVtb3JhbF9icC50aWZmIiwgcGxvdCA9IHAxLCB3aWR0aCA9IDE2LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQpgYGAKYGBge3J9CkdPX2VucmljaF9ET1dOX2h1bW9yYWxfQlAgPC0gZW5yaWNocGxvdDo6cGFpcndpc2VfdGVybXNpbShHT19lbnJpY2hfZG93bl9odW1vcmFsX2JwLCBtZXRob2QgPSAiSkMiKQoKcGx0IDwtIGVtYXBwbG90KEdPX2VucmljaF9ET1dOX2h1bW9yYWxfQlAsIAogICAgICAgICByZXBlbCA9IFRSVUUsCiAgICAgICAgIHNob3dDYXRlZ29yeSA9IDIwKSArCiAgZ2d0aXRsZSgiQmlvbG9naWNhbCBwcm9jZXNzZXMgZm9yIERPV05yZWd1bGF0ZWQgdGFyZ2V0cyBmb3IgaHVtb3JhbCIpICsKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMykKICAgICkgICAgCgpwbHQKCmdnc2F2ZSgiLi9waWN0dXJlc190cmFuc3BsL3RyYW5zcGxfR09fZW5yaWNobWVudF9lbWFwcGxvdF9ET1dOX2h1bW9yYWxfQlAudGlmZiIsIHBsb3QgPSBwbHQsIHdpZHRoID0gMTYsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCmBgYApgYGB7cn0KR09fZW5yaWNoX1VQMTUwX0JQIDwtIGVucmljaHBsb3Q6OnBhaXJ3aXNlX3Rlcm1zaW0oR09fZW5yaWNoX3VwMTUwX2JwLCBtZXRob2QgPSAiSkMiKQoKcGx0IDwtIGVtYXBwbG90KEdPX2VucmljaF9VUDE1MF9CUCwgCiAgICAgICAgIHJlcGVsID0gVFJVRSwKICAgICAgICAgc2hvd0NhdGVnb3J5ID0gMjApICsKICBnZ3RpdGxlKCJCaW9sb2dpY2FsIHByb2Nlc3NlcyBmb3IgVVByZWd1bGF0ZWQgdGFyZ2V0cyBmb3IgdmVzaWNsZXMgMTUwIikgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzKQogICAgKSAgIAoKcGx0CgpnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX0dPX2VucmljaG1lbnRfZW1hcHBsb3RfQlB1cF90eXBlMTUwLnRpZmYiLCBwbG90ID0gcGx0LCB3aWR0aCA9IDE2LCBoZWlnaHQgPSAxMCwgZHBpID0gMzAwKQpgYGAKKirQkNC90LDQu9C40Lcg0L7QsdC+0LPQsNGJ0LXQvdC40Y8g0LjQtyDQsdCw0LfRiyBJTU1VTkVTSUdEQioqCklNTVVORVNJR0RCOiDQndCw0LHQvtGA0Ysg0LPQtdC90L7Qsiwg0YHQstGP0LfQsNC90L3Ri9C1INGBINC40LzQvNGD0L3QvdC+0Lkg0YHQuNGB0YLQtdC80L7QuSwg0LLQutC70Y7Rh9Cw0Y8g0LjQvNC80YPQvdC+LdC+0L3QutC+0LvQvtCz0LjRjiDQuCDQtNGA0YPQs9C40LUg0LDRgdC/0LXQutGC0Ysg0LjQvNC80YPQvdC40YLQtdGC0LAuCmBgYHtyfQptc2lnX2dvX2JwIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJDNyIsIHN1YmNhdGVnb3J5ID0gIklNTVVORVNJR0RCIikKCkdPX2VucmljaF91cDE1MF9pbW11bSA8LSBlbnJpY2hlcihnZW5lID0gdGFyZ2V0czE1MF91cCwgVEVSTTJHRU5FID0gbXNpZ19nb19icFssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0pCkdPX2VucmljaF9kb3duMTUwX2ltbXVuIDwtIGVucmljaGVyKGdlbmUgPSB0YXJnZXRzMTUwX2Rvd24sIFRFUk0yR0VORSA9IG1zaWdfZ29fYnBbLCBjKCJnc19uYW1lIiwgImdlbmVfc3ltYm9sIildKQpgYGAKCioq0JLQuNC30YPQsNC70LjQt9Cw0YbQuNGPIElNTVVORVNJR0RCKioKYGBge3J9CgpwMSA8LSBkb3RwbG90KEdPX2VucmljaF91cDE1MF9pbW11bSwgc2hvd0NhdGVnb3J5ID0gMjApICsgCiAgZ2d0aXRsZSgiR08gRW5yaWNobWVudCBJTU1VTkVTSUdEIGZvciBVUHJlZ3VsYXRlZCB0YXJnZXRzIGZvciB2ZXNpY2xlcyAxNTAiKSArIAogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpCiAgKQoKcDIgPC0gZG90cGxvdChHT19lbnJpY2hfZG93bjE1MF9pbW11biwgc2hvd0NhdGVnb3J5ID0gMjApICsgCiAgZ2d0aXRsZSgiR08gRW5yaWNobWVudCBJTU1VTkVTSUdEIGZvciBET1dOcmVndWxhdGVkIHRhcmdldHMgZm9yIHZlc2ljbGVzIDE1MCIpICsgCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkKICApCgpwMSArIHAyCgpjb21iaW5lZF9wbG90IDwtIHAxICsgcDIKCmdnc2F2ZSgiLi9waWN0dXJlc190cmFuc3BsL3RyYW5zcGxfR09fZW5yaWNobWVudF9kb3RwbG90X0lNTVVORVNJR0RfdHlwZTE1MC50aWZmIiwgcGxvdCA9IGNvbWJpbmVkX3Bsb3QsIHdpZHRoID0gMTYsIGhlaWdodCA9IDEwLCBkcGkgPSAzMDApCmBgYAoKCioq0JDQvdCw0LvQuNC3INC+0LHQvtCz0LDRidC10L3QuNGPINC40Lcg0LHQsNC30YsgS0VHRyoqCmBgYHtyfQptc2lnX2dvX2JwIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJDMiIsIHN1YmNhdGVnb3J5ID0gIkNQOktFR0ciKQoKR09fZW5yaWNoX3VwMTUwX0tFR0cgPC0gZW5yaWNoZXIoZ2VuZSA9IHRhcmdldHMxNTBfdXAsIFRFUk0yR0VORSA9IG1zaWdfZ29fYnBbLCBjKCJnc19uYW1lIiwgImdlbmVfc3ltYm9sIildKQpHT19lbnJpY2hfZG93bjE1MF9LRUdHIDwtIGVucmljaGVyKGdlbmUgPSB0YXJnZXRzMTUwX2Rvd24sIFRFUk0yR0VORSA9IG1zaWdfZ29fYnBbLCBjKCJnc19uYW1lIiwgImdlbmVfc3ltYm9sIildKQoKcDEgPC0gZG90cGxvdChHT19lbnJpY2hfdXAxNTBfS0VHRywgc2hvd0NhdGVnb3J5ID0gMjApICsgCiAgZ2d0aXRsZSgiR08gRW5yaWNobWVudCBLRUdHIGZvciBVUHJlZ3VsYXRlZCB0YXJnZXRzIGZvciB2ZXNpY2xlcyAxNTAiKSArIAogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpCiAgKQoKcDIgPC0gZG90cGxvdChHT19lbnJpY2hfZG93bjE1MF9LRUdHLCBzaG93Q2F0ZWdvcnkgPSAyMCkgKyAKICBnZ3RpdGxlKCJHTyBFbnJpY2htZW50IEtFR0cgZm9yIERPV05yZWd1bGF0ZWQgdGFyZ2V0cyBmb3IgdmVzaWNsZXMgMTUwIikgKyAKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KQogICkKCnAxICsgcDIKCmNvbWJpbmVkX3Bsb3QgPC0gcDEgKyBwMgoKZ2dzYXZlKCIuL3BpY3R1cmVzX3RyYW5zcGwvdHJhbnNwbF9HT19lbnJpY2htZW50X2RvdHBsb3RfS0VHR190eXBlMTUwLnRpZmYiLCBwbG90ID0gY29tYmluZWRfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKYGBgCgoqKtCQ0L3QsNC70LjQtyDQvtCx0L7Qs9Cw0YnQtdC90LjRjyDQuNC3INCx0LDQt9GLIENQOldJS0lQQVRIV0FZUyoqCmBgYHtyfQptc2lnX2dvX2JwIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJDMiIsIHN1YmNhdGVnb3J5ID0gIkNQOldJS0lQQVRIV0FZUyIpCgpHT19lbnJpY2hfdXAxNTBfV0lLSSA8LSBlbnJpY2hlcihnZW5lID0gdGFyZ2V0czE1MF91cCwgVEVSTTJHRU5FID0gbXNpZ19nb19icFssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0pCkdPX2VucmljaF9kb3duMTUwX1dJS0kgPC0gZW5yaWNoZXIoZ2VuZSA9IHRhcmdldHMxNTBfZG93biwgVEVSTTJHRU5FID0gbXNpZ19nb19icFssIGMoImdzX25hbWUiLCAiZ2VuZV9zeW1ib2wiKV0pCgpwMSA8LSBkb3RwbG90KEdPX2VucmljaF91cDE1MF9XSUtJLCBzaG93Q2F0ZWdvcnkgPSAyMCkgKyAKICBnZ3RpdGxlKCJHTyBFbnJpY2htZW50IFdJS0lQQVRIV0FZUyBmb3IgVVByZWd1bGF0ZWQgdGFyZ2V0cyBmb3IgdmVzaWNsZXMgMTUwIikgKyAKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KQogICkKCnAyIDwtIGRvdHBsb3QoR09fZW5yaWNoX2Rvd24xNTBfV0lLSSwgc2hvd0NhdGVnb3J5ID0gMjApICsgCiAgZ2d0aXRsZSgiR08gRW5yaWNobWVudCBXSUtJUEFUSFdBWVMgZm9yIERPV05yZWd1bGF0ZWQgdGFyZ2V0cyBmb3IgdmVzaWNsZXMgMTUwIikgKyAKICB0aGVtZSgKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KQogICkKCnAxICsgcDIKY29tYmluZWRfcGxvdCA8LSBwMSArIHAyCgpnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX0dPX2VucmljaG1lbnRfZG90cGxvdF9XSUtJUEFUSFdBWV90eXBlMTUwLnRpZmYiLCBwbG90ID0gY29tYmluZWRfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gMTAsIGRwaSA9IDMwMCkKYGBgCiMjINCi0L7Qu9GM0LrQviDQstC10LfQuNC60YPQu9GLINGC0LjQv9CwIDE2CmBgYHtyfQpjb2xkYXRhXzE2IDwtIGNvbGRhdGFbY29sZGF0YSR0eXBlID09IDE2LCBdCnJvd25hbWVzKGNvbGRhdGFfMTYpIDwtIGNvbGRhdGFfMTYkc2FtcGxlCmNvbGRhdGFfMTYKYGBgCmBgYHtyfQpjb21tb25fc2FtcGxlc18xNiA8LSBpbnRlcnNlY3QoY29sbmFtZXMoY291bnRzKSwgY29sZGF0YV8xNiRzYW1wbGVzKQoKY291bnRzXzE2IDwtIGNvdW50c1ssIGMoY291bnRzJG1pUk5BLCBjb21tb25fc2FtcGxlcyldICAKY291bnRzXzE2IDwtIGNvdW50c18xNlssIHJvd25hbWVzKGNvbGRhdGFfMTYpXSAj0YDQsNC90LbQuNGA0YPRjiDQv9C+INC60L7Qu9C+0L3QutC4INCyIGNvdW50cyDRgtCw0Log0LbQtSDQutCw0Log0Lgg0L3QsNC30LLQsNC90LjRjyDRgdGC0YDQvtC6INCyIGNvbGRhdGFfMTUwCmhlYWQoY291bnRzXzE2KQpgYGAKKirQodC+0LfQtNCw0LXQvCBERVNlcURhdGFTZXQg0LjQtyDQvNCw0YLRgNC40YbRiyDQutCw0YPQvdGC0L7QsioqCmBgYHtyfQpkZHNfMTYgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudHNfMTYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gY29sZGF0YV8xNiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gMCArIHBhdGllbnQgKyBjb25kaXRpb24pCmRkc18xNiRjb25kaXRpb24gPC0gcmVsZXZlbChkZHNfMTYkY29uZGl0aW9uLCByZWYgPSAiYmVmb3JlIikKZGRzXzE2CmBgYAoqKtCk0LjQu9GM0YLRgNCw0YbQuNGPKioKYGBge3J9CmRpbShkZHNfMTYpCnNtYWxsZXN0R3JvdXBTaXplIDwtIDMKa2VlcCA8LSByb3dTdW1zKGNvdW50cyhkZHNfMTYpID49IDEwKSA+PSBzbWFsbGVzdEdyb3VwU2l6ZQpkZHNfMTYgPC0gZGRzXzE2W2tlZXAsXQpkaW0oZGRzXzE2KQpgYGAKCioqUnVuIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzIGZvciAxNTAgdHlwZSoqCmBgYHtyfQpkZHNfMTYgPC0gREVTZXEoZGRzXzE2LCBmaXRUeXBlID0gInBhcmFtZXRyaWMiKQpkZHNfMTYKYGBgCgpgYGB7cn0KcGxvdERpc3BFc3RzKGRkc18xNikKYGBgCmBgYHtyfQpyZXNfMTYgPC0gcmVzdWx0cyhkZHNfMTYsIGNvbnRyYXN0PWMoImNvbmRpdGlvbiIsICJiZWZvcmUiLCAiYWZ0ZXIiKSkKcmVzXzE2CmBgYAoqKk1BIHBsb3QqKgoK0KTQuNC70YzRgtGA0LDRhtC40Y8g0YLQvtGH0LXQuiDRgSDQvdC40LfQutC40Lwg0YHRgNC10LTQvdC40Lwg0Y3QutGB0L/RgNC10YHRgdC40YDQvtCy0LDQvdC40LXQvCAo0L/QviBiYXNlTWVhbikuCgnigKIJ0J7QsdGL0YfQvdC+INC+0YLRgdC10LrQsNGO0YLRgdGPIGJhc2VNZWFuIDwgMS4KCTIuCdCe0L/RgNC10LTQtdC70LXQvdC40LUg0LfQvdCw0YfQuNC80YvRhSDQs9C10L3QvtCyICjRgdC40L3QuNC1INGC0L7Rh9C60LgpOgoJ4oCiCdCY0YHQv9C+0LvRjNC30YPQtdGC0YHRjyDQutGA0LjRgtC10YDQuNC5IHBhZGogPCAwLjEg0L/QviDRg9C80L7Qu9GH0LDQvdC40Y4sINCwINC90LUgPCAwLjA1ISAKYGBge3J9CnRpZmYoIi4vcGljdHVyZXMvUGxvdE1BX3N0YW5kYXJ0X3BhZGpfMC4wNV90eXBlMTYudGlmZiIsIAogICAgIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLCByZXMgPSAzMDAsIGJnID0gIndoaXRlIikKcGxvdE1BKHJlc18xNiwgYWxwaGEgPSAwLjA1LCB5bGltID0gYygtOCwgOCkpIApkZXYub2ZmKCkKcGxvdE1BKHJlc18xNiwgYWxwaGEgPSAwLjA1LCB5bGltID0gYygtOCwgOCkpIApgYGAKKirQmtCw0YHRgtC+0LzQvdGL0LkgTUEgcGxvdCDQv9C+IHAtdmFsdWUqKgpgYGB7cn0KcmVzX2RmIDwtIHJlc18xNiAlPiUKICBhcy5kYXRhLmZyYW1lICU+JQogIG11dGF0ZShjb2xvciA9IGNhc2Vfd2hlbiggCiAgICBwdmFsdWUgPCAwLjA1ICYgIWlzLm5hKHB2YWx1ZSkgJiBhYnMobG9nMkZvbGRDaGFuZ2UpID4gMSAgfiAiYmx1ZSIsICAjINCX0L3QsNGH0LjQvNGL0LUg0L/QviBwLXZhbHVlINC4INC00LjRhCDRjdC60YHQv9GA0LXRgdGB0LjRgNC+0LLQsNC90L3Ri9C1CiAgICBUUlVFIH4gImdyYXk3MCIKICApKQoKcGx0IDwtIGdncGxvdChyZXNfZGYsIGFlcyh4ID0gYmFzZU1lYW4sIHkgPSBsb2cyRm9sZENoYW5nZSwgY29sb3IgPSBjb2xvcikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBzaXplID0gMSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gInNvbGlkIiwgY29sb3IgPSAiZ3JheTQwIiwgc2l6ZSA9IDEuNSkgKyAgIyDQlNC+0LHQsNCy0LvRj9C10Lwg0LvQuNC90LjRjgogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJncmF5NzAiID0gImdyYXk3MCIsICJibHVlIiA9ICJibHVlIikpICsKICBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6c2NpZW50aWZpYykgKyAKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoeCA9ICJtZWFuIG9mIG5vcm1hbGl6ZWQgY291bnRzIiwgeSA9ICJsb2cgZm9sZCBjaGFuZ2UiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcGx0Cmdnc2F2ZSgiLi9waWN0dXJlc190cmFuc3BsL3RyYW5zcGxfUGxvdE1BX2Nhc3RvbV9wdmFsdWUwLjA1X3R5cGUxNi50aWZmIiwgcGxvdCA9IHBsdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKYGBgCioq0JfQvdCw0YfQuNC80YvQtSDRgNC10LfRg9C70YzRgtCw0YLRiyoqCmBgYHtyfQpzdW1tYXJ5KHJlc3VsdHMoZGRzXzE2LCBjb250cmFzdD1jKCJjb25kaXRpb24iLCAiYmVmb3JlIiwgImFmdGVyIiksIGFscGhhPTAuMDUpKQpgYGAKTGV0J3MgYXJyYW5nZWQgaXQgYnkgbG9nMkZvbGRDaGFuZ2U6CmBgYHtyfQpvcmRlcl9pbmRpY2VzIDwtIG9yZGVyKC1yZXNfMTYkbG9nMkZvbGRDaGFuZ2UpCnJlc18xNltvcmRlcl9pbmRpY2VzLCBdCmBgYApWaXN1YWxpc2F0aW9uIGZvciB0aGUgZmlyc3QgZ2VuZQpgYGB7cn0KcGxvdENvdW50cyhkZHNfMTYsIGdlbmU9d2hpY2gubWF4KHJlc18xNiRsb2cyRm9sZENoYW5nZSksIGludGdyb3VwPSJjb25kaXRpb24iKQpwbG90Q291bnRzKGRkc18xNiwgZ2VuZT13aGljaC5taW4ocmVzXzE2JHB2YWx1ZSksIGludGdyb3VwPSJjb25kaXRpb24iKQojcGxvdENvdW50cyhkZHMsIGdlbmU9cm93bmFtZXMocmVzKVt3aGljaC5taW4ocmVzJHBhZGpbd2hpY2gubWF4KHJlcyRsb2cyRm9sZENoYW5nZSldKV0sIGludGdyb3VwPSJjb25kaXRpb24iKQpgYGAKKipWb2xjYW5vIHBsb3QqKgpgYGB7cn0KcGx0IDwtIEVuaGFuY2VkVm9sY2FubyhyZXNfMTYsCiAgICAgICAgICAgICAgICBsYWIgPSByb3duYW1lcyhyZXNfMTYpLAogICAgICAgICAgICAgICAgeCA9ICJsb2cyRm9sZENoYW5nZSIsCiAgICAgICAgICAgICAgICB5ID0gInB2YWx1ZSIsCiAgICAgICAgICAgICAgICBwQ3V0b2ZmID0gMC4wNSwKICAgICAgICAgICAgICAgIEZDY3V0b2ZmID0gMSwKICAgICAgICAgICAgICAgIGxhYlNpemUgPSAzLjAsCiAgICAgICAgICAgICAgICBib3hlZExhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICAgY29sID0gYygnYmxhY2snLCAnI0NCRDVFOCcsICcjQjNFMkNEJywgJyNGRENEQUMnKSwKICAgICAgICAgICAgICAgIGNvbEFscGhhID0gMSwKICAgICAgICAgICAgICAgIHRpdGxlID0gTlVMTCwgICAgICAgIAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBOVUxMKSAKCnBsdApnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX1ZvbGNhbm8gcGxvdF9iYXNlZF9vbl9QdmFsdWVfMTZ0eXBlLnRpZmYiLCBwbG90ID0gcGx0LCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCwgYmcgPSAid2hpdGUiKQpgYGAKKipybG9nINGC0YDQsNC90YHRhNC+0YDQvNCw0YbQuNGPKioKCeKAognQp9C10YDQvdGL0LUg0YLQvtGH0LrQuCDigJMg0YHRgtCw0L3QtNCw0YDRgtC90L7QtSDQvtGC0LrQu9C+0L3QtdC90LjQtSDQvtGC0LTQtdC70YzQvdGL0YUg0LPQtdC90L7Qsi4KCeKAognQmtGA0LDRgdC90LDRjyDQu9C40L3QuNGPIOKAkyDRgdCz0LvQsNC20LXQvdC90YvQuSDRgtGA0LXQvdC0INC30LDQstC40YHQuNC80L7RgdGC0LggU0Qg0L7RgiDRgdGA0LXQtNC90LXQs9C+INC30L3QsNGH0LXQvdC40Y8g0Y3QutGB0L/RgNC10YHRgdC40LguCgnigKIJ0JXRgdC70Lgg0LrRgNCw0YHQvdCw0Y8g0LvQuNC90LjRjyDQvdCw0LrQu9C+0L3QtdC90LAg0LLQstC10YDRhSDihpIg0YHRgtCw0L3QtNCw0YDRgtC90L7QtSDQvtGC0LrQu9C+0L3QtdC90LjQtSDRgNCw0YHRgtGR0YIg0YEg0YPQstC10LvQuNGH0LXQvdC40LXQvCDRgdGA0LXQtNC90LXQs9C+ICjQv9C70L7RhdCw0Y8g0L3QvtGA0LzQsNC70LjQt9Cw0YbQuNGPKS4KCeKAognQldGB0LvQuCDQutGA0LDRgdC90LDRjyDQu9C40L3QuNGPINC/0YDQuNC80LXRgNC90L4g0LPQvtGA0LjQt9C+0L3RgtCw0LvRjNC90LAg4oaSINC90L7RgNC80LDQu9C40LfQsNGG0LjRjyDRgdGA0LDQsdC+0YLQsNC70LAg0YXQvtGA0L7RiNC+LgpgYGB7cn0Kcmx0XzE2IDwtIHJsb2coZGRzXzE2KSAKbWVhblNkUGxvdChhc3NheShybHRfMTYpKSAgI9C/0L7QutCw0LfRi9Cy0LDQtdGCLCDQutCw0Log0LjQt9C80LXQvdGP0LXRgtGB0Y8g0YHRgtCw0L3QtNCw0YDRgtC90L7QtSDQvtGC0LrQu9C+0L3QtdC90LjQtSDQsiDQt9Cw0LLQuNGB0LjQvNC+0YHRgtC4INC+0YIg0YHRgNC10LTQvdC10LPQviDQt9C90LDRh9C10L3QuNGPINGN0LrRgdC/0YDQtdGB0YHQuNC4LgpgYGAKYGBge3J9CnBjYURhdGEgPC0gcGxvdFBDQShybHRfMTYsIGludGdyb3VwPWMoImNvbmRpdGlvbiIsICJwYXRpZW50IiksIHJldHVybkRhdGEgPSBUUlVFKQpwZXJjZW50VmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIocGNhRGF0YSwgInBlcmNlbnRWYXIiKSkKCmdncGxvdChwY2FEYXRhLCBhZXMoUEMxLCBQQzIsIHNoYXBlID0gcGF0aWVudCwgY29sb3IgPSBjb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHhsYWIocGFzdGUwKCJQQzE6ICIsIHBlcmNlbnRWYXJbMV0sICIlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLCBwZXJjZW50VmFyWzJdLCAiJSIpKSArIAogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiKQpgYGAKYGBge3J9CmFzc2F5KHJsdF8xNikgPC0gbGltbWE6OnJlbW92ZUJhdGNoRWZmZWN0KGFzc2F5KHJsdF8xNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhdGNoID0gY29sRGF0YShkZHNfMTYpWywncGF0aWVudCddKQoKcGNhRGF0YSA8LSBwbG90UENBKHJsdF8xNiwgaW50Z3JvdXA9YygiY29uZGl0aW9uIiwgInBhdGllbnQiKSwgcmV0dXJuRGF0YSA9IFRSVUUpCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoKcGx0IDwtIGdncGxvdChwY2FEYXRhLCBhZXMoUEMxLCBQQzIsIHNoYXBlID0gcGF0aWVudCwgY29sb3IgPSBjb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHhsYWIocGFzdGUwKCJQQzE6ICIsIHBlcmNlbnRWYXJbMV0sICIlIikpICsKICB5bGFiKHBhc3RlMCgiUEMyOiAiLCBwZXJjZW50VmFyWzJdLCAiJSIpKSArIAogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiKQoKcGx0Cmdnc2F2ZSgiLi9waWN0dXJlc190cmFuc3BsL3RyYW5zcGxfUENBIHBsb3QgZm9yIHR5cGUgMTYgYWZ0ZXIgcmVtb3ZpbmcgZG9ub3IgZWZmZWN0LnRpZmYiLCBwbG90ID0gcGx0LCB3aWR0aCA9IDgsIGhlaWdodCA9IDYsIGRwaSA9IDMwMCwgYmcgPSAid2hpdGUiKQpgYGAKKipQbG90IGEgaGVhdG1hcCBvZiB0aGUgbW9zdCBleHByZXNzZWQgZ2VuZXMqKgpgYGB7cn0KcmVzX3NpZ25fMTYgPC0gc3Vic2V0KHJlc18xNiwgcHZhbHVlIDwgMC4wNSAmICFpcy5uYShwdmFsdWUpICYgYWJzKGxvZzJGb2xkQ2hhbmdlKSA+IDEuMCkKcmVzX3NpZ25fMTYgPC0gcmVzX3NpZ25fMTZbb3JkZXIocmVzX3NpZ25fMTYkbG9nMkZvbGRDaGFuZ2UsIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQoKc2lnX2dlbmVzIDwtIHJvd25hbWVzKHJlc19zaWduXzE2KSAgIyDQn9C+0LvRg9GH0LDQtdC8INC40LzQtdC90LAg0LPQtdC90L7Qsiwg0LrQvtGC0L7RgNGL0LUg0L/RgNC+0YjQu9C4INGE0LjQu9GM0YLRgNCw0YbQuNGOCgpkZV9tYXQgPC0gYXNzYXkocmx0XzE2KVtzaWdfZ2VuZXMsIF0gCmRhdGFtYXRyaXggPC0gdChzY2FsZSh0KGRlX21hdCkpKQoKYW5ub3RhdGlvbl9jb2wgPC0gZGF0YS5mcmFtZShjb25kaXRpb24gPSBjb2xkYXRhXzE2JGNvbmRpdGlvbikKcm93bmFtZXMoYW5ub3RhdGlvbl9jb2wpIDwtIGNvbG5hbWVzKGRhdGFtYXRyaXgpCgphbm5vdGF0aW9uX2NvbG9ycyA8LSBsaXN0KAogIGNvbmRpdGlvbiA9IGMoImJlZm9yZSIgPSAiI0ZGQ0MwMCIsICJhZnRlciIgPSAiIzMzOTlGRiIpCikKCnBsdCA8LSBwaGVhdG1hcChkYXRhbWF0cml4LCAKICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCAKICAgICAgICAgY2x1c3Rlcl9jb2xzID0gVFJVRSwgCiAgICAgICAgIGFubm90YXRpb25fY29sID0gYW5ub3RhdGlvbl9jb2wsCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RhdGlvbl9jb2xvcnMsCiAgICAgICAgIGRpc3BsYXlfbnVtYmVycyA9IFRSVUUsCiAgICAgICAgIGxlZ2VuZCA9IEZBTFNFLAogICAgICAgICBmb250c2l6ZSA9IDE1KSAgCgpwbHQKZ2dzYXZlKCIuL3BpY3R1cmVzX3RyYW5zcGwvdHJhbnNwbF9IZWF0bWFwIG9mIGRpZmYgZXhwcmVzc2VkIGdlbmVzX3R5cGUxNi50aWZmIiwgcGxvdCA9IHBsdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKYGBgCioqUGxvdCBvZiB0aGUgZGlzdGFuY2UgYmV0d2VlbiBzYW1wbGVzIGhlYXRtYXAqKgrQoNCw0YHRh9C10YIg0YDQsNGB0YHRgtC+0Y/QvdC40Lkg0LzQtdC20LTRgyDQvtCx0YDQsNC30YbQsNC80LgKCeKAognQntCx0YvRh9C90L4g0LjRgdC/0L7Qu9GM0LfRg9C10YLRgdGPINC10LLQutC70LjQtNC+0LLQviDRgNCw0YHRgdGC0L7Rj9C90LjQtSAo0L/QviDRg9C80L7Qu9GH0LDQvdC40Y4g0LIgREVTZXEyKS4KCeKAognQntC90L4g0LLRi9GH0LjRgdC70Y/QtdGC0YHRjyDQv9C+INC90L7RgNC80LDQu9C40LfQvtCy0LDQvdC90YvQvCDQtNCw0L3QvdGL0Lwg0Y3QutGB0L/RgNC10YHRgdC40LggKHJsb2coKSDQuNC70LggdnN0KCkpLgoJ4oCiCdCn0LXQvCDQvNC10L3RjNGI0LUg0YDQsNGB0YHRgtC+0Y/QvdC40LUg4oCUINGC0LXQvCDQsdC+0LvQtdC1INC/0L7RhdC+0LbQuCDQvtCx0YDQsNC30YbRiy4KCmBgYHtyfQpzYW1wbGVEaXN0c18xNiA8LSBkaXN0KHQoYXNzYXkocmx0XzE2KSkpCnNhbXBsZURpc3RNYXRyaXhfMTYgPC0gYXMubWF0cml4KHNhbXBsZURpc3RzXzE2KQpyb3duYW1lcyhzYW1wbGVEaXN0TWF0cml4XzE2KSA8LSBwYXN0ZShybHRfMTYkY29uZGl0aW9uLCBybHRfMTYkcGF0aWVudCwgc2VwPSJfcGF0aWVudCIpCmNvbG5hbWVzKHNhbXBsZURpc3RNYXRyaXhfMTYpIDwtIHBhc3RlKHJsdF8xNiRjb25kaXRpb24sIHJsdF8xNiRwYXRpZW50LCBzZXA9Il9wYXRpZW50IikKCnBsdCA8LSBwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4XzE2LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIiwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImV1Y2xpZGVhbiIsCiAgICAgICAgIGZvbnRzaXplID0gMTIsCiAgICAgICAgIGxlZ2VuZCA9IEZBTFNFLAogICAgICAgICBkaXNwbGF5X251bWJlcnMgPSBUUlVFLAogICAgICAgICBjb2xvciA9IGNvbG9ycykKCnBsdApnZ3NhdmUoIi4vcGljdHVyZXNfdHJhbnNwbC90cmFuc3BsX1Bsb3Qgb2YgdGhlIGRpc3RhbmNlIGJldHdlZW4gc2FtcGxlc190eXBlMTYudGlmZiIsIHBsb3QgPSBwbHQsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAojIyMg0JDQvdCw0LvQuNC3INC+0LHQvtCz0LDRidC10L3QuNGPINC00LvRjyDQstC10LfQuNC60YPQuyDRgtC40L/QsCAxNiAjIyMKYGBge3J9CnVwXzE2IDwtIHJlc19zaWduXzE2ICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lIAogIGZpbHRlcihsb2cyRm9sZENoYW5nZSA+IDApCmRvd25fMTYgPC0gcmVzX3NpZ25fMTYgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgZmlsdGVyKGxvZzJGb2xkQ2hhbmdlIDwgMCkKcm93bmFtZXModXBfMTYpCnJvd25hbWVzKGRvd25fMTYpCmBgYArQn9C10YDQtdCy0L7QtNC40Lwg0LIgbWlSQmFzZQoJ4oCiCW1pUkJhc2U6IGh0dHBzOi8vd3d3Lm1pcmJhc2Uub3JnLwoJ4oCiCU1pckdlbmVEQjogaHR0cHM6Ly9taXJnZW5lZGIub3JnLwpgYGB7cn0KdXJsIDwtICJodHRwczovL21pcmdlbmVkYi5vcmcvYnJvd3NlL2hzYSIKcGFnZSA8LSByZWFkX2h0bWwodXJsKQpgYGAKCtCf0LDRgNGB0LjQvCDRgtCw0LHQu9C40YbRgwpgYGB7cn0KbWlyX3RhYmxlIDwtIHBhZ2UgJT4lCiAgaHRtbF9lbGVtZW50KCJ0YWJsZSIpICU+JQogIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpIAoKbWlyX3RhYmxlIDwtIG1pcl90YWJsZVstYygxOjMpLCBjKDEsMikgXSAKY29sbmFtZXMobWlyX3RhYmxlKSA8LSBjKCJNaXJHZW5lREJfSUQiLCAiTWlSQmFzZV9JRCIpCm1pcl90YWJsZSRNaXJHZW5lREJfSUQgPC0gc3ViKCIgViIsICIiLCBtaXJfdGFibGUkTWlyR2VuZURCX0lEKQoKaGVhZChtaXJfdGFibGUpCmBgYAoKYGBge3J9CnVwXzE2X2NsZWFuIDwtIHN1YigiXy4qIiwgIiIsIHJvdy5uYW1lcyh1cF8xNikpCnVwXzE2X2NvbnZlcnRlZCA8LSBtaXJfdGFibGUkTWlSQmFzZV9JRFttYXRjaCh1cF8xNl9jbGVhbiwgbWlyX3RhYmxlJE1pckdlbmVEQl9JRCldCgojZG93bl8xNl9jbGVhbiA8LSBzdWIoIl8uKiIsICIiLCByb3cubmFtZXMoZG93bl8xNikpCmRvd25fMTZfY29udmVydGVkIDwtIG1pcl90YWJsZSRNaVJCYXNlX0lEW21hdGNoKGRvd25fMTZfY2xlYW4sIG1pcl90YWJsZSRNaXJHZW5lREJfSUQpXQp1cF8xNl9jb252ZXJ0ZWQKZG93bl8xNl9jb252ZXJ0ZWQKYGBgCgoqKtCa0L7QvdCy0LXRgNGC0LDRhtC40Y8g0LIgTUlNQVRJRCoqIArQsiDQuNGC0L7Qs9C1INC30LDQvNC10L3QuNC70LAgTkEg0LLRgNGD0YfQvdGD0Y4g0L3QsCDRgdCw0LzRi9C1INCx0LvQuNC30LrQuNC1LCDQvdC+INGN0YLQviDRgtCw0LrQsNGPINGB0LXQsdC1INC/0YDQsNC60YLQuNC60LAKCk5BIEhzYS1NaXItMTAtUDNhXzVwICDQtdGB0YLRjCDQtNCy0LAg0YHQvtC+0YLQstC10YLRgdGC0LLQuNGPOgpIc2EtTWlyLTEwLVAxYyA9CWhzYS1taXItMTBhCkhzYS1NaXItMTAtUDNiID0gaHNhLW1pci0xMjVhCgpOQSBIc2EtTWlyLTEyNl8zcCog0LXRgdGC0Ywg0L7QtNC90L4g0YHQvtC+0YLQstC10YLRgdGC0LLQuNC1OiAKSHNhLU1pci0xMjYtUDIgPSBoc2EtbWlyLTEyNiAKCk5BIEhzYS1NaXItMzAtUDFjXzNwKiDQtdGB0YLRjCDRgtGA0Lgg0YHQvtC+0YLQstC10YLRgdGC0LLQuNGPOgpIc2EtTWlyLTMwLVAxYSA9IGhzYS1taXItMzBkCkhzYS1NaXItMzAtUDFiID0gaHNhLW1pci0zMGEKSHNhLU1pci0zMC1QMWQgPSBoc2EtbWlyLTMwZQoKWzFdICJIc2EtTWlyLTE4NV81cCIgICAgICJIc2EtTWlyLTE5N18zcCIgICAgICJIc2EtTWlyLTEwLVAzYV81cCIgICJIc2EtTWlyLTE0OC1QM181cCoiICJIc2EtTWlyLTMwLVAxYV8zcCoiICJIc2EtTWlyLTE1MF8zcCoiICAgCls3XSAiSHNhLU1pci0zNjFfM3AqIiAgICAiSHNhLU1pci0xMC1QMmJfNXAiICAiSHNhLU1pci0xMjZfM3AqIiAgIApbMV0gIkhzYS1NaXItMzQwXzVwIiAgICAgIkhzYS1NaXItMTgxLVAyY181cCIgIkhzYS1NaXItMjIzXzVwKiIgICAgIkhzYS1NaXItMzAtUDFjXzNwKiIKCk1JIChNaWNyb1JOQSBHZW5lIElEKSDigJQg0Y3RgtC+INC40LTQtdC90YLQuNGE0LjQutCw0YLQvtGAINC/0YDQtdC00YjQtdGB0YLQstC10L3QvdC40LrQsCAocHJlY3Vyc29yKSBtaVJOQQpNSU1BVCAoTWF0dXJlIG1pUk5BIElEKSDigJQg0Y3RgtC+INC40LTQtdC90YLQuNGE0LjQutCw0YLQvtGAINC30YDQtdC70L7QuSAobWF0dXJlKSBtaVJOQSwg0LrQvtGC0L7RgNCw0Y8g0YTRg9C90LrRhtC40L7QvdC40YDRg9C10YIg0LIg0LrQu9C10YLQutC1CmBgYHtyfQp1cF8xNl9jb252ZXJ0ZWQgPC0gIGMoImhzYS1taXItMTg1LTVwIiwgImhzYS1taXItMTk3LTNwIiwgImhzYS1taXItMTUyLTVwIiwgImhzYS1taXItMzBkLTNwIiwgImhzYS1taXItMTUwLTNwIiwgImhzYS1taXItMzYxLTNwIiwgImhzYS1taXItOTliLTVwIiwgImhzYS1taXItMTI2LTNwIikKZG93bl8xNl9jb252ZXJ0ZWQgPC0gYygiaHNhLW1pci0zNDAtNXAiLCAiaHNhLW1pci0xODFkLTVwIiwgImhzYS1taXItMjIzLTVwIikKCiMgdXBfMTZfY29udmVydGVkIDwtICBjKCJoc2EtbWlyLTE4NSIsICJoc2EtbWlyLTE5NyIsICJoc2EtbWlyLTE1MiIsICJoc2EtbWlyLTMwZCIsICJoc2EtbWlyLTE1MCIsICJoc2EtbWlyLTM2MSIsICJoc2EtbWlyLTk5YiIsICJoc2EtbWlyLTEyNiIpCiMgZG93bl8xNl9jb252ZXJ0ZWQgPC0gYygiaHNhLW1pci0zNDAiLCAiaHNhLW1pci0xODFkIiwgImhzYS1taXItMjIzIikgI9C90L4g0LXRgdGC0Ywg0YLQvtC70YzQutC+IE1JLCDQsCDQvdC1IE1JTUFUCgpjb252ZXJ0ZWRfbWlybmFfdXAxNiA8LSBtaVJOQVZlcnNpb25Db252ZXJ0KHVwXzE2X2NvbnZlcnRlZCkKY29udmVydGVkX21pcm5hX2Rvd24xNiA8LSBtaVJOQVZlcnNpb25Db252ZXJ0KGRvd25fMTZfY29udmVydGVkKQpjb252ZXJ0ZWRfbWlybmFfdXAxNgpjb252ZXJ0ZWRfbWlybmFfZG93bjE2CmBgYAoqKtCX0LDQv9GA0L7RgSDRgtCw0YDQs9C10YLQvtCyINC40Lcg0LHQsNC30YsgbXVsdGlNaVIqKgpgYGB7cn0KdGFyZ2V0czE2X3VwIDwtIHVuaXF1ZShnZXRfbXVsdGltaXIob3JnID0gImhzYSIsIG1pcm5hID0gY29udmVydGVkX21pcm5hX3VwMTYkQWNjZXNzaW9uLCB0YWJsZSA9ICJwcmVkaWN0ZWQiKUBkYXRhJHRhcmdldF9zeW1ib2wpCnRhcmdldHMxNl91cApgYGAKYGBge3J9CnRhcmdldHMxNl9kb3duIDwtIHVuaXF1ZShnZXRfbXVsdGltaXIob3JnID0gImhzYSIsIG1pcm5hID0gY29udmVydGVkX21pcm5hX2Rvd24xNiRBY2Nlc3Npb24sIHRhYmxlID0gInByZWRpY3RlZCIpQGRhdGEkdGFyZ2V0X3N5bWJvbCkKdGFyZ2V0czE2X2Rvd24KYGBgCgoK